Leaked source code of windows server 2003
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.
 
 
 
 
 
 

6352 lines
230 KiB

/*+
Copyright (c) 1989 Microsoft Corporation
Module Name:
san.c
Abstract:
Contains routines for SAN switch support
Author:
Vadim Eydelman (VadimE) 1-Jul-1998
Revision History:
--*/
#include "afdp.h"
VOID
AfdSanCancelConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
AfdSanCancelRequest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
PIRP
AfdSanDequeueRequest (
PAFD_ENDPOINT SanEndpoint,
PVOID RequestCtx
);
VOID
AfdSanInitEndpoint (
PAFD_ENDPOINT SanHlprEndpoint,
PFILE_OBJECT SanFile,
PAFD_SWITCH_CONTEXT SwitchContext
);
NTSTATUS
AfdSanNotifyRequest (
PAFD_ENDPOINT SanEndpoint,
PVOID RequestCtx,
NTSTATUS Status,
ULONG_PTR Information
);
NTSTATUS
AfdSanReferenceSwitchSocketByHandle (
IN HANDLE SocketHandle,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode,
IN PAFD_ENDPOINT SanHlprEndpoint,
IN PAFD_SWITCH_CONTEXT SwitchContext OPTIONAL,
OUT PFILE_OBJECT *FileObject
);
NTSTATUS
AfdSanDupEndpointIntoServiceProcess (
PFILE_OBJECT SanFileObject,
PVOID SavedContext,
ULONG ContextLength
);
NTSTATUS
AfdSanSetAskDupeToServiceState (
PAFD_ENDPOINT SanEndpoint
);
BOOLEAN
AfdSanSetDupingToServiceState (
PAFD_ENDPOINT SanEndpoint
);
BOOLEAN
AfdSanReferenceEndpointObject (
PAFD_ENDPOINT Endpoint
);
NTSTATUS
AfdSanFindSwitchSocketByProcessContext (
IN NTSTATUS Status,
IN PAFD_ENDPOINT SanHlprEndpoint,
IN PAFD_SWITCH_CONTEXT SwitchContext,
OUT PFILE_OBJECT *FileObject
);
VOID
AfdSanProcessAddrListForProviderChange (
PAFD_ENDPOINT SpecificEndpoint
);
NTSTATUS
AfdSanGetCompletionObjectTypePointer (
VOID
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE,AfdSanCreateHelper)
#pragma alloc_text (PAGE,AfdSanCleanupHelper)
#pragma alloc_text (PAGE,AfdSanCleanupEndpoint)
#pragma alloc_text (PAGE,AfdSanReferenceSwitchSocketByHandle)
#pragma alloc_text (PAGE,AfdSanFindSwitchSocketByProcessContext)
#pragma alloc_text (PAGESAN,AfdSanReferenceEndpointObject)
#pragma alloc_text (PAGE,AfdSanDupEndpointIntoServiceProcess)
#pragma alloc_text (PAGESAN,AfdSanSetAskDupeToServiceState)
#pragma alloc_text (PAGESAN,AfdSanSetDupingToServiceState)
#pragma alloc_text (PAGESAN,AfdSanFastCementEndpoint)
#pragma alloc_text (PAGESAN,AfdSanFastSetEvents)
#pragma alloc_text (PAGESAN,AfdSanFastResetEvents)
#pragma alloc_text (PAGESAN,AfdSanAcceptCore)
#pragma alloc_text (PAGESAN,AfdSanConnectHandler)
#pragma alloc_text (PAGESAN,AfdSanReleaseConnection)
#pragma alloc_text (PAGESAN,AfdSanFastCompleteAccept)
#pragma alloc_text (PAGESAN,AfdSanCancelAccept)
#pragma alloc_text (PAGESAN,AfdSanCancelConnect)
#pragma alloc_text (PAGESAN,AfdSanRedirectRequest)
#pragma alloc_text (PAGESAN,AfdSanFastCompleteRequest)
#pragma alloc_text (PAGE, AfdSanFastCompleteIo)
#pragma alloc_text (PAGESAN,AfdSanDequeueRequest)
#pragma alloc_text (PAGESAN,AfdSanCancelRequest)
#pragma alloc_text (PAGESAN,AfdSanFastRefreshEndpoint)
#pragma alloc_text (PAGE, AfdSanFastGetPhysicalAddr)
#pragma alloc_text (PAGE, AfdSanFastGetServicePid)
#pragma alloc_text (PAGE, AfdSanFastSetServiceProcess)
#pragma alloc_text (PAGE, AfdSanFastProviderChange)
#pragma alloc_text (PAGE, AfdSanAddrListChange)
#pragma alloc_text (PAGESAN, AfdSanProcessAddrListForProviderChange)
#pragma alloc_text (PAGE, AfdSanFastUnlockAll)
#pragma alloc_text (PAGE, AfdSanPollBegin)
#pragma alloc_text (PAGE, AfdSanPollEnd)
#pragma alloc_text (PAGESAN, AfdSanPollUpdate)
#pragma alloc_text (PAGE, AfdSanPollMerge)
#pragma alloc_text (PAGE, AfdSanFastTransferCtx)
#pragma alloc_text (PAGESAN, AfdSanAcquireContext)
#pragma alloc_text (PAGESAN, AfdSanInitEndpoint)
#pragma alloc_text (PAGE, AfdSanNotifyRequest)
#pragma alloc_text (PAGESAN, AfdSanRestartRequestProcessing)
#pragma alloc_text (PAGE, AfdSanGetCompletionObjectTypePointer)
#pragma alloc_text (PAGESAN, AfdSanAbortConnection)
#endif
//
// Dispatch level routines - external SAN entry points.
//
NTSTATUS
AfdSanCreateHelper (
PIRP Irp,
PFILE_FULL_EA_INFORMATION EaBuffer,
PAFD_ENDPOINT *Endpoint
)
/*++
Routine Description:
Allocates and initializes SAN helper endpoint for communication between switch
and AFD.
Arguments:
Irp - Create IRP
EaBuffer - Create IRP Ea buffer (AFD_SWITCH_OPEN_PACKET structure)
CompletionPort - completion port to reflect kernel calls to switch
CompletionEvent - event to identify overlapped IO triggered by the
switch as opposed to the application
Endpoint - buffer to place created endpoint pointer.
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_ACCESS_VIOLATION - incorrect input buffer size.
other - failed to access port/event object or allocation failure..
--*/
{
NTSTATUS status;
HANDLE port, event;
PVOID ioCompletionPort;
PVOID ioCompletionEvent;
if ( !MmIsThisAnNtAsSystem () ) {
#ifndef DONT_CHECK_FOR_DTC
return STATUS_NOT_SUPPORTED;
#else
DbgPrint ("AFD: Temporarily allowing SAN support on non-server build\n");
#endif //DONT_CHECK_FOR_DTC
}
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_SWITCH_OPEN_PACKET32 openPacket32;
if (EaBuffer->EaValueLength<sizeof (*openPacket32)) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCreateHelper: Invalid switch open packet size.\n"));
}
return STATUS_ACCESS_VIOLATION;
}
openPacket32 = (PAFD_SWITCH_OPEN_PACKET32)(EaBuffer->EaName +
EaBuffer->EaNameLength + 1);
event = openPacket32->CompletionEvent;
port = openPacket32->CompletionPort;
}
else
#endif //_WIN64
{
PAFD_SWITCH_OPEN_PACKET openPacket;
if (EaBuffer->EaValueLength<sizeof (*openPacket)) {
IF_DEBUG (SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCreateHelper: Invalid switch open packet size.\n"));
}
return STATUS_ACCESS_VIOLATION;
}
openPacket = (PAFD_SWITCH_OPEN_PACKET)(EaBuffer->EaName +
EaBuffer->EaNameLength + 1);
event = openPacket->CompletionEvent;
port = openPacket->CompletionPort;
}
if (IoCompletionObjectType==NULL) {
status = AfdSanGetCompletionObjectTypePointer ();
if (!NT_SUCCESS (status)) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdSanCreateHelper: Could not get completion OT:%lx\n",
status));
}
return status;
}
}
//
// Get references to completion port and event
//
status = ObReferenceObjectByHandle (
port,
IO_COMPLETION_ALL_ACCESS,
IoCompletionObjectType,
Irp->RequestorMode,
&ioCompletionPort,
NULL
);
if (!NT_SUCCESS (status)) {
IF_DEBUG (SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCreateHelper: Could not reference completion port (%p).\n",
status));
}
return status;
}
status = ObReferenceObjectByHandle (
event,
EVENT_ALL_ACCESS,
*ExEventObjectType,
Irp->RequestorMode,
&ioCompletionEvent,
NULL
);
if (!NT_SUCCESS (status)) {
ObDereferenceObject (ioCompletionPort);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCreateHelper: Could not reference completion event (%p).\n",
status));
}
return status;
}
//
// Allocate an AFD "helper" endpoint.
//
status = AfdAllocateEndpoint(
Endpoint,
NULL,
0
);
if( !NT_SUCCESS(status) ) {
ObDereferenceObject (ioCompletionPort);
ObDereferenceObject (ioCompletionEvent);
return status;
}
(*Endpoint)->Type = AfdBlockTypeSanHelper;
(*Endpoint)->Common.SanHlpr.IoCompletionPort = ioCompletionPort;
(*Endpoint)->Common.SanHlpr.IoCompletionEvent = ioCompletionEvent;
(*Endpoint)->Common.SanHlpr.Plsn = 0;
(*Endpoint)->Common.SanHlpr.PendingRequests = 0;
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
if (AfdSanCodeHandle==NULL) {
AfdSanCodeHandle = MmLockPagableCodeSection( (PVOID)AfdSanFastCementEndpoint );
ASSERT( AfdDiscardableCodeHandle != NULL );
InitializeListHead (&AfdSanHelperList);
}
InsertTailList (&AfdSanHelperList, &(*Endpoint)->Common.SanHlpr.SanListLink);
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
//
// HACKHACK. Force IO subsystem to call us when last handle to the file is closed
// in any given process.
//
IoGetCurrentIrpStackLocation (Irp)->FileObject->LockOperation = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS
AfdSanFastCementEndpoint (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Changes the endpoint type to SAN to indicate that
it is used for support of user mode SAN providers
Associates switch context with the endpoint.
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_CEMENT_SAN)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_CONTEXT_INFO)
SocketHandle - handle of the endpoint being changed to SAN
SwitchContext - switch context associated with the endpoint
InputBufferLength - sizeof(AFD_SWITCH_CONTEXT_INFO)
OutputBuffer - unused
OutputBufferLength - unused
Information - pointer for buffer to place return information into, unused
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint or switch socket is of incorrect type
STATUS_INVALID_PARAMETER - input buffer is of incorrect size
other - failed when attempting to access switch socket, input buffer, or switch context.
--*/
{
NTSTATUS status;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PFILE_OBJECT sanFileObject;
AFD_SWITCH_CONTEXT_INFO contextInfo;
PAFD_ENDPOINT sanEndpoint, sanHlprEndpoint;
PVOID context;
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
*Information = 0;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_CONTEXT_INFO32 contextInfo32;
if (InputBufferLength<sizeof (*contextInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*contextInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_CONTEXT_INFO32));
}
contextInfo32 = InputBuffer;
contextInfo.SocketHandle = contextInfo32->SocketHandle;
contextInfo.SwitchContext = UlongToPtr(contextInfo32->SwitchContext);
}
else
#endif _WIN64
{
if (InputBufferLength<sizeof (contextInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (contextInfo),
PROBE_ALIGNMENT (AFD_SWITCH_CONTEXT_INFO));
}
contextInfo = *((PAFD_SWITCH_CONTEXT_INFO)InputBuffer);
}
if (contextInfo.SwitchContext==NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Switch context is NULL in AfdSanFastCementEndpoint\n"));
}
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForWrite (contextInfo.SwitchContext,
sizeof (*contextInfo.SwitchContext),
PROBE_ALIGNMENT (AFD_SWITCH_CONTEXT));
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
contextInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
NULL,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
return status;
}
sanEndpoint = sanFileObject->FsContext;
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdFastCementSanEndpoint: endp-%p, hlpr-%p.\n",
sanEndpoint, sanHlprEndpoint));
}
//
// Make sure that san endpoint is in the state where it can
// be given to the san provider.
//
if (AFD_PREVENT_STATE_CHANGE (sanEndpoint)) { // Prevents state change
context = AfdLockEndpointContext (sanEndpoint); // Locks out select
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle); // Locks out cleanup
// from looking at partially
// initialized data.
if (!sanEndpoint->EndpointCleanedUp &&
(sanEndpoint->Type==AfdBlockTypeEndpoint) &&
(sanEndpoint->State==AfdEndpointStateBound) ) {
AFD_SWITCH_CONTEXT localContext = {0,0,0,0};
AfdSanInitEndpoint (sanHlprEndpoint, sanFileObject, contextInfo.SwitchContext);
sanEndpoint->DisableFastIoSend = TRUE;
sanEndpoint->DisableFastIoRecv = TRUE;
sanEndpoint->EnableSendEvent = TRUE;
sanEndpoint->Common.SanEndp.SelectEventsActive = AFD_POLL_SEND;
sanEndpoint->State = AfdEndpointStateConnected;
sanEndpoint->Common.SanEndp.LocalContext = &localContext;
AfdIndicateEventSelectEvent (sanEndpoint, AFD_POLL_CONNECT|AFD_POLL_SEND, STATUS_SUCCESS);
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent (sanEndpoint, AFD_POLL_CONNECT|AFD_POLL_SEND, STATUS_SUCCESS);
status = AfdSanPollMerge (sanEndpoint, &localContext);
sanEndpoint->Common.SanEndp.LocalContext = NULL;
}
else {
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
status = STATUS_INVALID_HANDLE;
}
AfdUnlockEndpointContext (sanEndpoint, context);
AFD_REALLOW_STATE_CHANGE (sanEndpoint);
}
else {
status = STATUS_INVALID_HANDLE;
}
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanFastCementEndpoint, status: 0x%lX", status);
ObDereferenceObject (sanFileObject);
return status;
}
NTSTATUS
AfdSanFastSetEvents (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Sets the poll event on the san endpoint to report
to the application via various forms of the select
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_SET_EVENTS)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_EVENT_INFO)
SocketHandle - handle of the endpoint being changed to SAN
EventBit - event bit to set
Status - associated status (for AFD_POLL_EVENT_CONNECT_FAIL)
InputBufferLength - sizeof(AFD_SWITCH_EVENT_INFO)
OutputBuffer - unused
OutputBufferLength - unused
Information - pointer for buffer to place return information into, unused
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint or switch socket is of incorrect type
STATUS_INVALID_PARAMETER - input buffer is of incorrect size, invalid event bit.
other - failed when attempting to access switch socket, input buffer, or switch context.
--*/
{
NTSTATUS status;
PFILE_OBJECT sanFileObject;
AFD_SWITCH_EVENT_INFO eventInfo;
PAFD_ENDPOINT sanEndpoint, sanHlprEndpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
*Information = 0;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_EVENT_INFO32 eventInfo32;
if (InputBufferLength<sizeof (*eventInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*eventInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_EVENT_INFO32));
}
eventInfo32 = InputBuffer;
eventInfo.SocketHandle = eventInfo32->SocketHandle;
eventInfo.SwitchContext = UlongToPtr(eventInfo32->SwitchContext);
eventInfo.EventBit = eventInfo32->EventBit;
eventInfo.Status = eventInfo32->Status;
}
else
#endif _WIN64
{
if (InputBufferLength<sizeof (eventInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (eventInfo),
PROBE_ALIGNMENT (AFD_SWITCH_EVENT_INFO));
}
eventInfo = *((PAFD_SWITCH_EVENT_INFO)InputBuffer);
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
if (eventInfo.EventBit >= AFD_NUM_POLL_EVENTS) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Invalid EventBit=%d passed to AfdSanFastSetEvents\n",
eventInfo.EventBit));
}
return STATUS_INVALID_PARAMETER;
}
eventInfo.Status = AfdValidateStatus (eventInfo.Status);
//
// If event is connect failure, then context should not exist.
// If event is not connect failure, context should exist.
//
if ((eventInfo.EventBit==AFD_POLL_CONNECT_FAIL_BIT) ^
(eventInfo.SwitchContext==NULL)) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: AfdSanFastSetEvents-bit:%ld, context:%p inconsistent\n",
eventInfo.EventBit,
eventInfo.SwitchContext));
}
return STATUS_INVALID_PARAMETER;
}
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
eventInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
eventInfo.SwitchContext,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
return status;
}
sanEndpoint = sanFileObject->FsContext;
//
// Prevent endpoint reuse
//
if (!AFD_PREVENT_STATE_CHANGE (sanEndpoint)) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
if (sanEndpoint->State==AfdEndpointStateConnected ||
(eventInfo.EventBit==AFD_POLL_CONNECT_FAIL_BIT &&
sanEndpoint->State==AfdEndpointStateBound) ) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdFastSetSanEvents: endp-%p, bit-%lx, status-%lx.\n",
sanEndpoint, eventInfo.EventBit, eventInfo.Status));
}
try {
LONG currentEvents, newEvents;
//
// Update our event record. Make sure endpoint is connected, otherwise
// sanEndpoint->Common.SanEndp.SwitchContext will not be valid
//
if (sanEndpoint->State==AfdEndpointStateConnected) {
do {
currentEvents = *((LONG volatile *)&sanEndpoint->Common.SanEndp.SelectEventsActive);
newEvents = *((LONG volatile *)&sanEndpoint->Common.SanEndp.SwitchContext->EventsActive);
}
while (InterlockedCompareExchange (
(PLONG)&sanEndpoint->Common.SanEndp.SelectEventsActive,
newEvents,
currentEvents)!=currentEvents);
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete_state_change;
}
//
// Signal the event.
//
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
AfdIndicateEventSelectEvent (sanEndpoint, 1<<eventInfo.EventBit, eventInfo.Status);
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent (sanEndpoint, 1<<eventInfo.EventBit, eventInfo.Status);
status = STATUS_SUCCESS;
}
else {
status = STATUS_INVALID_HANDLE;
}
complete_state_change:
AFD_REALLOW_STATE_CHANGE (sanEndpoint);
complete:
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdFastSetEvents, event/status: 0x%lX",
NT_SUCCESS (status) ? eventInfo.EventBit : status);
ObDereferenceObject (sanFileObject);
return status;
}
NTSTATUS
AfdSanFastResetEvents (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Resets the poll event on the san endpoint so that it is no
longer reported to the application via various forms of the select
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_RESET_EVENTS)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_EVENT_INFO)
SocketHandle - handle of the endpoint being changed to SAN
EventBit - event bit to reset
Status - associated status (ignored)
InputBufferLength - sizeof(AFD_SWITCH_EVENT_INFO)
OutputBuffer - unused
OutputBufferLength - unused
Information - pointer for buffer to place return information into, unused
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint or switch socket is of incorrect type
STATUS_INVALID_PARAMETER - input buffer is of incorrect size, invalid event bit.
other - failed when attempting to access switch socket, input buffer, or switch context.
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
NTSTATUS status;
PFILE_OBJECT sanFileObject;
AFD_SWITCH_EVENT_INFO eventInfo;
PAFD_ENDPOINT sanEndpoint, sanHlprEndpoint;
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
*Information = 0;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_EVENT_INFO32 eventInfo32;
if (InputBufferLength<sizeof (*eventInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*eventInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_EVENT_INFO32));
}
eventInfo32 = InputBuffer;
eventInfo.SocketHandle = eventInfo32->SocketHandle;
eventInfo.SwitchContext = UlongToPtr(eventInfo32->SwitchContext);
eventInfo.EventBit = eventInfo32->EventBit;
eventInfo.Status = eventInfo32->Status;
}
else
#endif _WIN64
{
if (InputBufferLength<sizeof (eventInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (eventInfo),
PROBE_ALIGNMENT (AFD_SWITCH_EVENT_INFO));
}
eventInfo = *((PAFD_SWITCH_EVENT_INFO)InputBuffer);
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
if (eventInfo.EventBit >= AFD_NUM_POLL_EVENTS) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Invalid EventBit=%d passed to AfdSanFastResetEvents\n",
eventInfo.EventBit));
}
return STATUS_INVALID_PARAMETER;
}
if (eventInfo.SwitchContext==NULL) {
KdPrint (("AFD: Switch context is NULL in AfdSanFastResetEvents\n"));
return STATUS_INVALID_PARAMETER;
}
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
eventInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
eventInfo.SwitchContext,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
return status;
}
sanEndpoint = sanFileObject->FsContext;
//
// Prevent endpoint reuse
//
if (!AFD_PREVENT_STATE_CHANGE (sanEndpoint)) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
if (!IS_SAN_ENDPOINT (sanEndpoint)) {
goto complete_state_change;
}
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdFastResetSanEvents: endp-%p, bit-%lx, status-%lx.\n",
sanEndpoint, eventInfo.EventBit, eventInfo.Status));
}
try {
LONG currentEvents, newEvents;
//
// Update our event record.
//
do {
currentEvents = *((LONG volatile *)&sanEndpoint->Common.SanEndp.SelectEventsActive);
newEvents = *((LONG volatile *)&sanEndpoint->Common.SanEndp.SwitchContext->EventsActive);
}
while (InterlockedCompareExchange (
(PLONG)&sanEndpoint->Common.SanEndp.SelectEventsActive,
newEvents,
currentEvents)!=currentEvents);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete_state_change;
}
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
//
// Reset EventSelect mask
sanEndpoint->EventsActive &= (~ (1<<(eventInfo.EventBit)));
if (eventInfo.EventBit == AFD_POLL_SEND_BIT)
sanEndpoint->EnableSendEvent = TRUE;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
status = STATUS_SUCCESS;
complete_state_change:
AFD_REALLOW_STATE_CHANGE (sanEndpoint);
complete:
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdFastResetEvents, event/status: 0x%lX",
NT_SUCCESS (status) ? eventInfo.EventBit : status);
ObDereferenceObject (sanFileObject);
return status;
}
//
// Macros to make the super accept restart code more maintainable.
//
#define AfdRestartSuperAcceptInfo DeviceIoControl
// Used while IRP is in AFD queue (otherwise AfdAcceptFileObject
// is stored as completion routine context).
#define AfdAcceptFileObject Type3InputBuffer
// Used when IRP is passed to the transport (otherwise MdlAddress
// is stored in the IRP itself).
#define AfdMdlAddress Type3InputBuffer
#define AfdReceiveDataLength OutputBufferLength
#define AfdRemoteAddressLength InputBufferLength
#define AfdLocalAddressLength IoControlCode
NTSTATUS
FASTCALL
AfdSanConnectHandler (
PIRP Irp,
PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Implements connect indication from SAN provider.
Picks up the accept from the listening endpoint queue
or queues the IRP an signals the application to come
down with an accept.
Arguments:
Irp - SAN connect IRP
IrpSp - stack location
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
PAFD_SWITCH_CONNECT_INFO connectInfo;
union {
#ifdef _WIN64
PAFD_SWITCH_ACCEPT_INFO32 acceptInfo32;
#endif //_WIN64
PAFD_SWITCH_ACCEPT_INFO acceptInfo;
} u;
PFILE_OBJECT listenFileObject;
PAFD_ENDPOINT sanHlprEndpoint;
PAFD_ENDPOINT listenEndpoint;
PAFD_CONNECTION connection;
ULONG RemoteAddressLength;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PIRP acceptIrp;
PTA_ADDRESS localAddress;
listenFileObject = NULL;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_SWITCH_CONNECT_INFO newSystemBuffer;
PAFD_SWITCH_CONNECT_INFO32 oldSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG newLength;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(*oldSystemBuffer) ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
newLength = (sizeof(*newSystemBuffer) - sizeof(*oldSystemBuffer));
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength > (MAXULONG - newLength)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
newLength += IrpSp->Parameters.DeviceIoControl.InputBufferLength;
try {
newSystemBuffer = ExAllocatePoolWithQuotaTag (
NonPagedPool|POOL_RAISE_IF_ALLOCATION_FAILURE,
newLength,
AFD_SYSTEM_BUFFER_POOL_TAG);
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode ();
goto complete;
}
newSystemBuffer->ListenHandle = oldSystemBuffer->ListenHandle;
newSystemBuffer->SwitchContext = UlongToPtr(oldSystemBuffer->SwitchContext);
RtlMoveMemory (&newSystemBuffer->RemoteAddress,
&oldSystemBuffer->RemoteAddress,
IrpSp->Parameters.DeviceIoControl.InputBufferLength-
FIELD_OFFSET (AFD_SWITCH_CONNECT_INFO32, RemoteAddress));
ExFreePool (Irp->AssociatedIrp.SystemBuffer);
Irp->AssociatedIrp.SystemBuffer = newSystemBuffer;
IrpSp->Parameters.DeviceIoControl.InputBufferLength = newLength;
}
#endif // _WIN64
//
// Set up local variables.
//
sanHlprEndpoint = IrpSp->FileObject->FsContext;
ASSERT( sanHlprEndpoint->Type == AfdBlockTypeSanHelper);
Irp->IoStatus.Information = 0;
connectInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Verify input parameters
//
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof (*connectInfo) ||
connectInfo->RemoteAddress.TAAddressCount!=2 || // Must have local and remote addresses
(IrpSp->Parameters.DeviceIoControl.InputBufferLength <
FIELD_OFFSET (AFD_SWITCH_CONNECT_INFO,
RemoteAddress.Address[0].Address[
connectInfo->RemoteAddress.Address[0].AddressLength])+sizeof(TA_ADDRESS))) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof (*u.acceptInfo32)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
}
else
#endif // _WIN64
{
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof (*u.acceptInfo)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
}
RemoteAddressLength = FIELD_OFFSET (TRANSPORT_ADDRESS, Address[0].Address[
connectInfo->RemoteAddress.Address[0].AddressLength]);
localAddress = (PTA_ADDRESS)
&(connectInfo->RemoteAddress.Address[0].Address[
connectInfo->RemoteAddress.Address[0].AddressLength]);
if (&localAddress->Address[localAddress->AddressLength]-(PUCHAR)Irp->AssociatedIrp.SystemBuffer>
(LONG)IrpSp->Parameters.DeviceIoControl.InputBufferLength) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
if (!IS_SAN_HELPER(sanHlprEndpoint) ||
sanHlprEndpoint->OwningProcess!=IoGetCurrentProcess ()) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
try {
if (connectInfo->SwitchContext == NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Switch context is NULL in AfdSanConnectHandler\n"));
}
ExRaiseStatus(STATUS_INVALID_PARAMETER);
}
if (Irp->RequestorMode != KernelMode) {
ProbeForWrite(connectInfo->SwitchContext,
sizeof(*connectInfo->SwitchContext),
PROBE_ALIGNMENT(AFD_SWITCH_CONTEXT));
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
goto complete;
}
//
// We will separate addresses, so change the count
//
connectInfo->RemoteAddress.TAAddressCount = 1;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
u.acceptInfo32 = MmGetMdlVirtualAddress (Irp->MdlAddress);
ASSERT (u.acceptInfo32!=NULL);
ASSERT (MmGetMdlByteCount (Irp->MdlAddress)>=sizeof (*u.acceptInfo32));
}
else
#endif // _WIN64
{
u.acceptInfo = MmGetMdlVirtualAddress (Irp->MdlAddress);
ASSERT (u.acceptInfo!=NULL);
ASSERT (MmGetMdlByteCount (Irp->MdlAddress)>=sizeof (*u.acceptInfo));
}
//
// Get the listening file object and verify its type and state
//
status = ObReferenceObjectByHandle (
connectInfo->ListenHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode >> 14) & 3, // DesiredAccess
*IoFileObjectType,
Irp->RequestorMode,
(PVOID)&listenFileObject,
NULL);
if (!NT_SUCCESS (status)) {
goto complete;
}
if (IoGetRelatedDeviceObject (listenFileObject)!=AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
listenEndpoint = listenFileObject->FsContext;
if ( !listenEndpoint->Listening ||
listenEndpoint->State == AfdEndpointStateClosing ||
listenEndpoint->EndpointCleanedUp ) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanConnectHandler: endp-%p, irp-%p.\n",
listenEndpoint,
Irp));
}
if (!IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint)) {
//
// Keep getting accept IRPs/connection structures till
// we find one that can be used to satisfy connect indication
// or queue it.
//
while ((connection = AfdGetFreeConnection( listenEndpoint, &acceptIrp ))!=NULL
&& acceptIrp!=NULL) {
PAFD_ENDPOINT acceptEndpoint;
PFILE_OBJECT acceptFileObject;
PIO_STACK_LOCATION irpSp;
IF_DEBUG(LISTEN) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanConnectHandler: using connection %lx\n",
connection ));
}
ASSERT( connection->Type == AfdBlockTypeConnection );
irpSp = IoGetCurrentIrpStackLocation (acceptIrp);
acceptFileObject = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdAcceptFileObject;
acceptEndpoint = acceptFileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
ASSERT (acceptIrp->Tail.Overlay.DriverContext[0] == connection);
ASSERT (connection->Endpoint == NULL);
InterlockedDecrement (
&listenEndpoint->Common.VcListening.FailedConnectionAdds);
InterlockedPushEntrySList (
&listenEndpoint->Common.VcListening.FreeConnectionListHead,
&connection->SListEntry
);
DEBUG connection = NULL;
//
// Make sure connection indication comes from current process.
// (we do check it indirectly up above when validating the request.
// This check is explicit).
//
if (IoThreadToProcess (Irp->Tail.Overlay.Thread)==IoGetCurrentProcess ()) {
//
// Check if super accept Irp has enough space for
// the remote address
//
if( (ULONG)RemoteAddressLength <=
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength ) {
//
// Check if we have enough system PTE's to map
// the buffer.
//
status = AfdMapMdlChain (acceptIrp->MdlAddress);
if( NT_SUCCESS (status) ) {
HANDLE acceptHandle;
BOOLEAN handleDuplicated;
if (IoThreadToProcess (Irp->Tail.Overlay.Thread)==
IoThreadToProcess (acceptIrp->Tail.Overlay.Thread)) {
acceptHandle = acceptIrp->Tail.Overlay.DriverContext[3];
status = STATUS_SUCCESS;
handleDuplicated = FALSE;
}
else {
//
// Listen process is different than the accepting one.
// We need to duplicate accepting handle into the listening
// process so that accept can take place there and the accepting
// socket will later get dup-ed into the accepting process when
// that process performs an IO operation on it.
//
status = ObOpenObjectByPointer (
acceptFileObject,
OBJ_CASE_INSENSITIVE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
KernelMode,
&acceptHandle);
handleDuplicated = TRUE; // If we fail duplication above,
// this variable is not used
// so setting it to TRUE won't
// have any effect.
}
if (NT_SUCCESS (status)) {
AfdAcquireSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
if (!acceptEndpoint->EndpointCleanedUp) {
IoSetCancelRoutine (acceptIrp, AfdSanCancelAccept);
if (!acceptIrp->Cancel) {
//
// Copy the remote address from the connection object
//
#ifndef i386
if (acceptEndpoint->Common.VcConnecting.FixAddressAlignment) {
USHORT addressLength =
connectInfo->RemoteAddress.Address[0].AddressLength
+ sizeof (USHORT);
USHORT UNALIGNED *pAddrLength = (PVOID)
((PUCHAR)MmGetSystemAddressForMdl (acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength
- sizeof (USHORT));
RtlMoveMemory (
(PUCHAR)MmGetSystemAddressForMdl (acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
&connectInfo->RemoteAddress.Address[0].AddressType,
addressLength);
*pAddrLength = addressLength;
}
else
#endif
{
RtlMoveMemory (
(PUCHAR)MmGetSystemAddressForMdl (acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
&connectInfo->RemoteAddress,
RemoteAddressLength);
}
if (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0) {
TDI_ADDRESS_INFO UNALIGNED *addressInfo = (PVOID)
((PUCHAR)MmGetSystemAddressForMdl(acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength);
#ifndef i386
if (acceptEndpoint->Common.VcConnecting.FixAddressAlignment) {
USHORT UNALIGNED * pAddrLength = (PVOID)
((PUCHAR)addressInfo
+irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
-sizeof(USHORT));
RtlMoveMemory (
addressInfo,
&localAddress->AddressType,
localAddress->AddressLength+sizeof (USHORT));
*pAddrLength = localAddress->AddressLength+sizeof (USHORT);
}
else
#endif
{
addressInfo->ActivityCount = 0;
addressInfo->Address.TAAddressCount = 1;
RtlMoveMemory (
&addressInfo->Address.Address,
localAddress,
FIELD_OFFSET (TA_ADDRESS, Address[localAddress->AddressLength]));
}
}
ASSERT (acceptEndpoint->Irp==acceptIrp);
acceptEndpoint->Irp = NULL;
//
// Convert endpoint to SAN
//
AfdSanInitEndpoint (sanHlprEndpoint, acceptFileObject, connectInfo->SwitchContext);
UPDATE_ENDPOINT2 (acceptEndpoint,
"AfdSanConnectHandler, accepted with bytes: 0x%d",
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength);
InsertTailList (&acceptEndpoint->Common.SanEndp.IrpList,
&acceptIrp->Tail.Overlay.ListEntry);
//
// Setup output for switch and complete its IRP
//
// Do this under protection of exception handler since application
// can change protection attributes of the virtual address range
// or even deallocate it.
try {
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
u.acceptInfo32->AcceptHandle = (VOID * POINTER_32)acceptHandle;
u.acceptInfo32->ReceiveLength = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength;
Irp->IoStatus.Information = sizeof (*u.acceptInfo32);
}
else
#endif //_WIN64
{
u.acceptInfo->AcceptHandle = acceptHandle;
u.acceptInfo->ReceiveLength = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength;
Irp->IoStatus.Information = sizeof (*u.acceptInfo);
}
}
except (AFD_EXCEPTION_FILTER_NO_STATUS()) {
//
// If the app is playing with switch's virtual addresses
// we can't help much - it's accept IRP will probably
// just hang since the switch is not going to follow
// the failed connect IRP with accept completion.
//
}
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
AfdRecordConnectionsPreaccepted ();
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp, AfdPriorityBoost);
ObDereferenceObject (listenFileObject);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanConnectHandler: pre-accepted, endp-%p, SuperAccept irp-%p\n",
acceptEndpoint, acceptIrp));
}
return STATUS_SUCCESS;
}
else { //if (!acceptIrp->Cancel
if (IoSetCancelRoutine (acceptIrp, NULL)==NULL) {
KIRQL cancelIrql;
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
else {
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
}
}
}
else { // if (!acceptEndpoint->EndpointCleanedUp)
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanConnectHandler: accept endpoint cleanedup. endp=%lx",
acceptEndpoint));
}
}
if (handleDuplicated) {
#if DBG
status =
#endif
NtClose (acceptHandle);
}
ASSERT (NT_SUCCESS (status));
status = STATUS_CANCELLED;
} // if (!accept handle duplication succeeded)
} // if (!MDL mapping succeeded).
}
else {
status = STATUS_BUFFER_TOO_SMALL;
}
}
else {
status = STATUS_INVALID_HANDLE;
}
UPDATE_ENDPOINT2 (acceptEndpoint,
"AfdSanConnectHandler, Superaccept failed with status: 0x%lX",
status);
AfdCleanupSuperAccept (acceptIrp, status);
IoCompleteRequest (acceptIrp, AfdPriorityBoost);
}
}
else {
//
// We have little choice but create an extra connection
// on the fly since regular connection are posted to
// the transport as TDI_LISTENs.
//
status = AfdCreateConnection(
listenEndpoint->TransportInfo,
listenEndpoint->AddressHandle,
IS_TDI_BUFFERRING(listenEndpoint),
listenEndpoint->InLine,
listenEndpoint->OwningProcess,
&connection
);
if (!NT_SUCCESS (status)) {
goto complete;
}
InterlockedDecrement (
&listenEndpoint->Common.VcListening.FailedConnectionAdds);
}
if (connection!=NULL) {
LIST_ENTRY irpList;
ASSERT (connection->Endpoint == NULL);
if ( connection->RemoteAddress != NULL &&
connection->RemoteAddressLength < (ULONG)RemoteAddressLength ) {
AFD_RETURN_REMOTE_ADDRESS(
connection->RemoteAddress,
connection->RemoteAddressLength
);
connection->RemoteAddress = NULL;
}
if ( connection->RemoteAddress == NULL ) {
connection->RemoteAddress = AFD_ALLOCATE_REMOTE_ADDRESS (RemoteAddressLength);
if (connection->RemoteAddress==NULL) {
AfdSanReleaseConnection (listenEndpoint, connection, TRUE);
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
}
connection->RemoteAddressLength = RemoteAddressLength;
RtlMoveMemory(
connection->RemoteAddress,
&connectInfo->RemoteAddress,
RemoteAddressLength
);
//
// We just got a connection without AcceptEx IRP
// We'll have to queue the IRP, setup cancel routine and pend it
//
AfdAcquireSpinLock (&listenEndpoint->SpinLock, &lockHandle);
//
// Setup the connection, so cancel routine can
// operate on it properly.
//
connection->ConnectIrp = NULL;
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = connection;
IoSetCancelRoutine (Irp, AfdSanCancelConnect);
if (Irp->Cancel) {
if (IoSetCancelRoutine (Irp, NULL)==NULL) {
KIRQL cancelIrql;
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
//
// Cancel routine is running, let it complete
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
else {
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
}
AfdSanReleaseConnection (listenEndpoint, connection, TRUE);
status = STATUS_CANCELLED;
goto complete;
}
IoMarkIrpPending (Irp);
connection->Endpoint = listenEndpoint;
REFERENCE_ENDPOINT (listenEndpoint);
connection->ConnectIrp = Irp;
connection->SanConnection = TRUE;
connection->State = AfdConnectionStateUnaccepted;
InitializeListHead (&irpList);
//
// Try to find AcceptEx or Listen IRP to complete.
//
while (1) {
PIRP waitForListenIrp;
if (!IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint)) {
if (AfdServiceSuperAccept (listenEndpoint, connection, &lockHandle, &irpList)) {
goto CompleteIrps;
}
}
//
// Complete listen IRPs until we find the one that has enough space
// for the remote address.
//
if (IsListEmpty( &listenEndpoint->Common.VcListening.ListeningIrpListHead ) )
break;
//
// Get a pointer to the current IRP, and get a pointer to the
// current stack lockation.
//
waitForListenIrp = CONTAINING_RECORD(
listenEndpoint->Common.VcListening.ListeningIrpListHead.Flink,
IRP,
Tail.Overlay.ListEntry
);
//
// Take the first IRP off the listening list.
//
RemoveEntryList(
&waitForListenIrp->Tail.Overlay.ListEntry
);
waitForListenIrp->Tail.Overlay.ListEntry.Flink = NULL;
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanConnectHandler: completing IRP %lx\n",
waitForListenIrp ));
}
status = AfdServiceWaitForListen (waitForListenIrp,
connection,
&lockHandle);
if (NT_SUCCESS (status)) {
ObDereferenceObject (listenFileObject);
return STATUS_PENDING;
}
//
// Synchronize with cancel routine if it is running
//
if (IoSetCancelRoutine (waitForListenIrp, NULL)==NULL) {
KIRQL cancelIrql;
//
// The cancel routine won't find the IRP on the list
// Just make sure it completes before we complete the IRP.
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
IoCompleteRequest (waitForListenIrp, AfdPriorityBoost);
AfdAcquireSpinLock (&listenEndpoint->SpinLock, &lockHandle);
}
//
// At this point, we still hold the AFD spinlock.
// and we could find matching listen request.
// Put the connection on unaccepted list.
//
InsertTailList(
&listenEndpoint->Common.VcListening.UnacceptedConnectionListHead,
&connection->ListEntry
);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanConnectHandler: unaccepted, conn-%p\n",
connection));
}
//
// Listening endpoint is never a specifically SAN endpoint.
// Poll/EventSelect events on it are handled like on a regular
// TCP/IP endpoint - no need for special tricks like on connected/accepted
// endpoints.
//
AfdIndicateEventSelectEvent(
listenEndpoint,
AFD_POLL_ACCEPT,
STATUS_SUCCESS
);
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
//
// If there are outstanding polls waiting for a connection on this
// endpoint, complete them.
//
AfdIndicatePollEvent(
listenEndpoint,
AFD_POLL_ACCEPT,
STATUS_SUCCESS
);
CompleteIrps:
//
// Complete previously failed accept irps if any.
//
while (!IsListEmpty (&irpList)) {
PIRP irp;
irp = CONTAINING_RECORD (irpList.Flink, IRP, Tail.Overlay.ListEntry);
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
IoCompleteRequest (irp, AfdPriorityBoost);
}
ObDereferenceObject (listenFileObject);
return STATUS_PENDING;
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
UPDATE_ENDPOINT2 (listenEndpoint,
"AfdSanConnectHandler, accept failed with status: 0x%lX",
status);
complete:
if (listenFileObject!=NULL) {
ObDereferenceObject (listenFileObject);
}
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, AfdPriorityBoost);
return status;
}
NTSTATUS
AfdSanFastCompleteAccept (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Completes the Accept operation initiated by the SAN provider
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_CMPL_ACCEPT)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_CONTEXT_INFO)
SocketHandle - handle of the accepting endpoint
SwitchContext - switch context associated with the endpoint
InputBufferLength - sizeof(AFD_SWITCH_CONTEXT_INFO)
OutputBuffer - data to copy into the AcceptEx receive buffer
OutputBufferLength - size of received data
Information - pointer for buffer to place return information into, unused
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint or switch socket is of incorrect type
STATUS_INVALID_PARAMETER - input buffer is of incorrect size.
STATIS_LOCAL_DISCONNECT - accept was aborted by the application.
other - failed when attempting to access accept socket, input buffer, or switch context.
--*/
{
NTSTATUS status;
PIRP acceptIrp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PFILE_OBJECT sanFileObject;
AFD_SWITCH_CONTEXT_INFO contextInfo;
PAFD_ENDPOINT sanEndpoint, sanHlprEndpoint;
PVOID context;
*Information = 0;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_CONTEXT_INFO32 contextInfo32;
if (InputBufferLength<sizeof (*contextInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*contextInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_CONTEXT_INFO32));
}
contextInfo32 = InputBuffer;
contextInfo.SocketHandle = contextInfo32->SocketHandle;
contextInfo.SwitchContext = UlongToPtr(contextInfo32->SwitchContext);
}
else
#endif _WIN64
{
if (InputBufferLength<sizeof (contextInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (contextInfo),
PROBE_ALIGNMENT (AFD_SWITCH_CONTEXT_INFO));
}
contextInfo = *((PAFD_SWITCH_CONTEXT_INFO)InputBuffer);
}
if (contextInfo.SwitchContext==NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Switch context is NULL in AfdSanFastCompleteAccept\n"));
}
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForWrite (contextInfo.SwitchContext,
sizeof (*contextInfo.SwitchContext),
PROBE_ALIGNMENT (AFD_SWITCH_CONTEXT));
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
contextInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
contextInfo.SwitchContext,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
return status;
}
sanEndpoint = sanFileObject->FsContext;
//
// Make sure that endpoints are of correct type
//
context = AfdLockEndpointContext (sanEndpoint);
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
if (!sanEndpoint->EndpointCleanedUp &&
sanEndpoint->State==AfdEndpointStateOpen) {
//
// See if accept IRP is still there
//
if (!IsListEmpty (&sanEndpoint->Common.SanEndp.IrpList)) {
AFD_SWITCH_CONTEXT localContext = {0,0,0,0};
acceptIrp = CONTAINING_RECORD (
sanEndpoint->Common.SanEndp.IrpList.Flink,
IRP,
Tail.Overlay.ListEntry);
RemoveEntryList (&acceptIrp->Tail.Overlay.ListEntry);
acceptIrp->Tail.Overlay.ListEntry.Flink = NULL;
sanEndpoint->Common.SanEndp.SelectEventsActive = AFD_POLL_SEND;
sanEndpoint->State = AfdEndpointStateConnected;
sanEndpoint->DisableFastIoSend = TRUE;
sanEndpoint->DisableFastIoRecv = TRUE;
sanEndpoint->EnableSendEvent = TRUE;
ASSERT (sanEndpoint->Common.SanEndp.LocalContext==NULL);
sanEndpoint->Common.SanEndp.LocalContext = &localContext;
AFD_END_STATE_CHANGE (sanEndpoint);
AfdIndicateEventSelectEvent (sanEndpoint, AFD_POLL_SEND, STATUS_SUCCESS);
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent (sanEndpoint, AFD_POLL_SEND, STATUS_SUCCESS);
status = AfdSanPollMerge (sanEndpoint, &localContext);
sanEndpoint->Common.SanEndp.LocalContext = NULL;
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdFastSanCompleteAccept: endp-%p, irp-%p\n",
sanEndpoint,
acceptIrp));
}
if (IoSetCancelRoutine (acceptIrp, NULL)==NULL) {
KIRQL cancelIrql;
//
// Irp is being cancelled, sync up with cancel routine
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
//
// Copy receive data if passed and IRP has buffer for it
//
if ((OutputBufferLength>0) && (acceptIrp->MdlAddress!=NULL)) {
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
ULONG bytesCopied;
NTSTATUS tdiStatus;
tdiStatus = TdiCopyBufferToMdl (
OutputBuffer,
0,
OutputBufferLength,
acceptIrp->MdlAddress,
0,
&bytesCopied);
ASSERT (NT_SUCCESS (tdiStatus));
*Information = bytesCopied;
acceptIrp->IoStatus.Information = bytesCopied;
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
//
// Even if copy failed, we still have to complete
// the accept IRP because we have already removed
// cancel routine and modified endpoint state.
//
}
}
else {
acceptIrp->IoStatus.Information = 0;
}
acceptIrp->IoStatus.Status = status;
//
// Complete the accept IRP.
//
IoCompleteRequest (acceptIrp, AfdPriorityBoost);
//
// undo the references done in AfdAcceptCore()
//
ASSERT( InterlockedDecrement( &sanEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject (sanFileObject);
}
else {
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
status = STATUS_LOCAL_DISCONNECT;
}
}
else {
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
status = STATUS_INVALID_HANDLE;
}
AfdUnlockEndpointContext (sanEndpoint, context);
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanFastCompletAccept, status: %lX", status);
ObDereferenceObject (sanFileObject); // undo reference we did earlier in this routine
return status;
}
//
// Macros to make request/context passing code readable.
//
#define AfdSanRequestInfo Tail.Overlay
#define AfdSanRequestCtx DriverContext[0]
#define AfdSanSwitchCtx DriverContext[1]
#define AfdSanProcessId DriverContext[2]
#define AfdSanHelperEndp DriverContext[3]
NTSTATUS
FASTCALL
AfdSanRedirectRequest (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Redirects file system Read/Write IRP to SAN provider
Arguments:
Irp - the to be redirected.
IrpSp - current stack location
Return Value:
Status of the redirect operation.
--*/
{
PAFD_ENDPOINT sanEndpoint;
NTSTATUS status;
AFD_LOCK_QUEUE_HANDLE lockHandle;
ULONG_PTR requestInfo;
ULONG requestType;
PVOID requestCtx;
BOOLEAN postRequest;
Irp->IoStatus.Information = 0;
//
// Get the endpoint and validate it.
//
sanEndpoint = IrpSp->FileObject->FsContext;
//
// Make sure Irp has not been cancelled meanwhile
//
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
if (!IS_SAN_ENDPOINT (sanEndpoint) ||
sanEndpoint->State!=AfdEndpointStateConnected) {
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
status = STATUS_INVALID_CONNECTION;
goto complete;
}
if (sanEndpoint->Common.SanEndp.CtxTransferStatus!=STATUS_PENDING &&
sanEndpoint->Common.SanEndp.CtxTransferStatus!=STATUS_MORE_PROCESSING_REQUIRED) {
if (!NT_SUCCESS (sanEndpoint->Common.SanEndp.CtxTransferStatus)) {
status = sanEndpoint->Common.SanEndp.CtxTransferStatus;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
goto complete;
}
//
// Get the request information based on IRP MJ code
//
switch (IrpSp->MajorFunction) {
case IRP_MJ_READ:
requestType = AFD_SWITCH_REQUEST_READ;
requestInfo = IrpSp->Parameters.Read.Length;
break;
case IRP_MJ_WRITE:
requestType = AFD_SWITCH_REQUEST_WRITE;
requestInfo = IrpSp->Parameters.Write.Length;
break;
default:
ASSERT (!"Unsupported IRP Major Function");
status = STATUS_INVALID_DEVICE_REQUEST;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
goto complete;
}
//
// Generate request context that uniquely identifies
// it among other requests on the same endpoint.
//
requestCtx = AFD_SWITCH_MAKE_REQUEST_CONTEXT(
sanEndpoint->Common.SanEndp.RequestId,
requestType);
sanEndpoint->Common.SanEndp.RequestId += 1;
//
// Store the request context in the Irp and insert it into
// the list
//
Irp->AfdSanRequestInfo.AfdSanRequestCtx = requestCtx;
postRequest = TRUE;
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdSanRedirectRequest, pended request: 0x%lX",
PtrToUlong (requestCtx));
}
else {
postRequest = FALSE;
AFD_W4_INIT requestInfo = 0; // Depend on variable above, but compiler
AFD_W4_INIT requestCtx = NULL;// does not see the connection.
Irp->AfdSanRequestInfo.AfdSanRequestCtx = NULL;
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdSanRedirectRequest, request suspended due to pending dup: 0x%lX",
PtrToUlong (Irp));
}
IoSetCancelRoutine (Irp, AfdSanCancelRequest);
if (Irp->Cancel) {
//
// Oops, let it go
//
Irp->Tail.Overlay.ListEntry.Flink = NULL;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
if (IoSetCancelRoutine (Irp, NULL)==NULL) {
KIRQL cancelIrql;
//
// Cancel routine must be running, make sure
// it complete before we complete the IRP
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
status = STATUS_CANCELLED;
goto complete;
}
//
// We are going to pend this IRP, mark it so
//
IoMarkIrpPending (Irp);
InsertTailList (&sanEndpoint->Common.SanEndp.IrpList,
&Irp->Tail.Overlay.ListEntry);
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanRedirectRequest: endp-%p, irp-%p, context-%p\n",
sanEndpoint, Irp, requestCtx));
}
if (postRequest) {
status = AfdSanNotifyRequest (sanEndpoint, requestCtx, STATUS_SUCCESS, requestInfo);
if (!NT_SUCCESS (status)) {
PIRP irp;
//
// If notification failed, fail the request.
// Note that we cannot return the failure status directly
// as we already marked IRP as pending. Also, the IRP
// could have been cancelled, so we have to search for
// it in the list.
//
irp = AfdSanDequeueRequest (sanEndpoint, requestCtx);
if (irp!=NULL) {
ASSERT (irp==Irp);
irp->IoStatus.Status = status;
IoCompleteRequest (irp, AfdPriorityBoost);
}
}
}
return STATUS_PENDING;
complete:
//
// Failure before we queued the IRP, complete and return
// status to the caller.
//
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, AfdPriorityBoost);
return status;
}
NTSTATUS
AfdSanFastCompleteRequest (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Completes the redirected read/write request processed by SAN provider
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_CMPL_ACCEPT)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_REQUEST_INFO)
SocketHandle - SAN endpoint on which to complete the request
SwitchContext - switch context associated with endpoint
to validate the handle-endpoint association
RequestContext - value that identifies the request to complete
RequestStatus - status with which to complete the request (
STATUS_PENDING has special meaning, request
is not completed - merely data is copied)
DataOffset - offset in the request buffer to read/write the data
InputBufferLength - sizeof (AFD_SWITCH_REQUEST_INFO)
OutputBuffer - switch buffer to read/write data
OutputBufferLength - length of the buffer
Information - pointer to buffer to return number of bytes copied
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper or SAN endpoint is of incorrect type
STATUS_INVALID_PARAMETER - input buffer is of incorrect size.
STATUS_CANCELLED - request to be completed has already been cancelled
other - failed when attempting to access SAN endpoint, input buffer or output buffers.
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
PIRP irp;
ULONG bytesCopied;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PFILE_OBJECT sanFileObject;
AFD_SWITCH_REQUEST_INFO requestInfo;
PAFD_ENDPOINT sanEndpoint, sanHlprEndpoint;
*Information = 0;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_REQUEST_INFO32 requestInfo32;
if (InputBufferLength<sizeof (*requestInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*requestInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_REQUEST_INFO32));
}
requestInfo32 = InputBuffer;
requestInfo.SocketHandle = requestInfo32->SocketHandle;
requestInfo.SwitchContext = UlongToPtr(requestInfo32->SwitchContext);
requestInfo.RequestContext = UlongToPtr(requestInfo32->RequestContext);
requestInfo.RequestStatus = requestInfo32->RequestStatus;
requestInfo.DataOffset = requestInfo32->DataOffset;
}
else
#endif _WIN64
{
if (InputBufferLength<sizeof (requestInfo)) {
return STATUS_INVALID_PARAMETER;
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (requestInfo),
PROBE_ALIGNMENT (AFD_SWITCH_REQUEST_INFO));
}
requestInfo = *((PAFD_SWITCH_REQUEST_INFO)InputBuffer);
}
if (requestInfo.SwitchContext==NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Switch context is NULL in AfdSanFastCompleteRequest\n"));
}
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
requestInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
requestInfo.SwitchContext,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
return status;
}
sanEndpoint = sanFileObject->FsContext;
//
// Find and dequeue the request in question
//
irp = AfdSanDequeueRequest (sanEndpoint, requestInfo.RequestContext);
if (irp!=NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanFastCompleteRequest: endp-%p, irp-%p, context-%p, status-%lx\n",
sanEndpoint, irp,
requestInfo.RequestContext,
requestInfo.RequestStatus));
}
//
// Expect the operation to succeed
//
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
//
// Get IRP stack location and perform data copy
//
irpSp = IoGetCurrentIrpStackLocation (irp);
switch (irpSp->MajorFunction) {
case IRP_MJ_READ:
//
// Read request, data is copied from switch buffer
// to the request MDL
//
ASSERT (AFD_SWITCH_REQUEST_TYPE(requestInfo.RequestContext)==AFD_SWITCH_REQUEST_READ);
if (NT_SUCCESS (requestInfo.RequestStatus)) {
if (irp->MdlAddress!=NULL &&
MmGetMdlByteCount (irp->MdlAddress)>requestInfo.DataOffset) {
try {
if (RequestorMode!=KernelMode) {
ProbeForRead (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
}
status = TdiCopyBufferToMdl (
OutputBuffer,
0,
OutputBufferLength,
irp->MdlAddress,
requestInfo.DataOffset,
&bytesCopied
);
*Information = bytesCopied;
ASSERT (irp->IoStatus.Information==requestInfo.DataOffset);
irp->IoStatus.Information += bytesCopied;
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
}
else if (irp->MdlAddress==NULL &&
requestInfo.DataOffset==0 &&
OutputBufferLength==0) {
ASSERT (irp->IoStatus.Information==0);
ASSERT (status==STATUS_SUCCESS);
}
else {
//
// Indicate to the switch that offset
// is outside of the buffer
//
status = STATUS_INVALID_PARAMETER;
}
}
break;
case IRP_MJ_WRITE:
//
// Write request, data is copied to switch buffer
// from the request MDL
//
ASSERT (AFD_SWITCH_REQUEST_TYPE(requestInfo.RequestContext)==AFD_SWITCH_REQUEST_WRITE);
if (NT_SUCCESS (requestInfo.RequestStatus)) {
if (irp->MdlAddress!=NULL &&
MmGetMdlByteCount (irp->MdlAddress)>requestInfo.DataOffset) {
try {
if (RequestorMode!=KernelMode) {
ProbeForWrite (OutputBuffer,
OutputBufferLength,
sizeof (UCHAR));
}
status = TdiCopyMdlToBuffer (
irp->MdlAddress,
requestInfo.DataOffset,
OutputBuffer,
0,
OutputBufferLength,
&bytesCopied
);
*Information = bytesCopied;
ASSERT (irp->IoStatus.Information==requestInfo.DataOffset);
irp->IoStatus.Information += bytesCopied;
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
}
else if (irp->MdlAddress==NULL &&
requestInfo.DataOffset==0 &&
OutputBufferLength==0) {
ASSERT (irp->IoStatus.Information==0);
ASSERT (status==STATUS_SUCCESS);
}
else {
//
// Indicate to the switch that offset
// is outside of the buffer
//
status = STATUS_INVALID_PARAMETER;
}
}
break;
default:
ASSERT (!"Unsupported IRP Major Function");
status = STATUS_INVALID_DEVICE_REQUEST;
}
//
// If switch did not ask to pend the request, complete it
//
if (NT_SUCCESS (status) && requestInfo.RequestStatus!=STATUS_PENDING) {
//
// Prepeare the request for completion
//
irp->IoStatus.Status = AfdValidateStatus (requestInfo.RequestStatus);
IoCompleteRequest (irp, AfdPriorityBoost);
}
else {
//
// Otherwise, put it back into the queue
//
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
IoSetCancelRoutine (irp, AfdSanCancelRequest);
//
// Of course, we need to make sure that request
// was not cancelled while we were processing it.
//
if (!irp->Cancel) {
InsertHeadList (&sanEndpoint->Common.SanEndp.IrpList,
&irp->Tail.Overlay.ListEntry);
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
}
else {
//
// Request has already been cancelled
//
ASSERT (irp->Tail.Overlay.ListEntry.Flink == NULL);
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
if (IoSetCancelRoutine (irp, NULL)==NULL) {
KIRQL cancelIrql;
//
// Cancel routine is running, synchronize with
// it
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
//
// Complete the request and indicate to the
// switch that it was cancelled
//
irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest (irp, AfdPriorityBoost);
status = STATUS_CANCELLED;
}
}
}
else {
//
// Could not find the request, it must have been
// cancelled already
//
status = STATUS_CANCELLED;
}
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanFastCompleteRequest, status: 0x%lX", status);
ObDereferenceObject (sanFileObject);
return status;
}
NTSTATUS
AfdSanFastCompleteIo (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Simulates async IO completion for the switch.
Arguments:
FileObject - SAN endpoint on which to complete the IO
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_CMPL_IO)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (IO_STATUS_BLOCK)
Status - final operation status
Information - associated information (number of bytes
transferred to/from request buffer(s))
InputBufferLength - sizeof (IO_STATUS_BLOCK)
OutputBuffer - unused
OutputBufferLength - unused
Information - pointer to buffer to return number of bytes transferred
Return Value:
STATUS_INVALID_PARAMETER - input buffer is of invalid size.
other - status of the IO operation or failure code when attempting to access input buffer.
--*/
{
NTSTATUS status;
#if !DBG
UNREFERENCED_PARAMETER (FileObject);
#endif
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
PAGED_CODE ();
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
if (InputBufferLength>=sizeof (IO_STATUS_BLOCK32)) {
// Carefully write status info
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (IO_STATUS_BLOCK32),
PROBE_ALIGNMENT32 (IO_STATUS_BLOCK32));
}
*Information = ((PIO_STATUS_BLOCK32)InputBuffer)->Information;
status = AfdValidateStatus (((PIO_STATUS_BLOCK32)InputBuffer)->Status);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
}
else {
status = STATUS_INVALID_PARAMETER;
}
}
else
#endif //_WIN64
{
if (InputBufferLength>=sizeof (IO_STATUS_BLOCK)) {
// Carefully write status info
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (IO_STATUS_BLOCK),
PROBE_ALIGNMENT (IO_STATUS_BLOCK));
}
*Information = ((PIO_STATUS_BLOCK)InputBuffer)->Information;
status = AfdValidateStatus (((PIO_STATUS_BLOCK)InputBuffer)->Status);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
}
else {
status = STATUS_INVALID_PARAMETER;
}
}
UPDATE_ENDPOINT2 (FileObject->FsContext, "AfdSanFastCompletIo, status: 0x%lX", status);
return status;
}
NTSTATUS
AfdSanFastRefreshEndpoint (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Refreshes endpoint so it can be used again in AcceptEx
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_REFRESH_ENDP)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_CONTEXT_INFO)
SocketHandle - SAN endpoint on which to referesh
SwitchContext - switch context associated with endpoint
to validate the handle-endpoint association
InputBufferLength - unused
OutputBuffer - unused
OutputBufferLength - unused
Information - pointer for buffer to place return information into, unused
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint or switch socket is of incorrect type
other - failed when attempting to access SAN socket.
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
KIRQL oldIrql;
NTSTATUS status;
PFILE_OBJECT sanFileObject;
AFD_SWITCH_CONTEXT_INFO contextInfo;
PAFD_ENDPOINT sanEndpoint, sanHlprEndpoint;
PVOID context;
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
*Information = 0;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_CONTEXT_INFO32 contextInfo32;
if (InputBufferLength<sizeof (*contextInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*contextInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_CONTEXT_INFO32));
}
contextInfo32 = InputBuffer;
contextInfo.SocketHandle = contextInfo32->SocketHandle;
contextInfo.SwitchContext = UlongToPtr(contextInfo32->SwitchContext);
}
else
#endif _WIN64
{
if (InputBufferLength<sizeof (contextInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (contextInfo),
PROBE_ALIGNMENT (AFD_SWITCH_CONTEXT_INFO));
}
contextInfo = *((PAFD_SWITCH_CONTEXT_INFO)InputBuffer);
}
if (contextInfo.SwitchContext==NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Switch context is NULL in AfdSanFastRefereshEndpoint\n"));
}
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForWrite (contextInfo.SwitchContext,
sizeof (*contextInfo.SwitchContext),
PROBE_ALIGNMENT (AFD_SWITCH_CONTEXT));
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
return status;
}
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
contextInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
contextInfo.SwitchContext,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
return status;
}
sanEndpoint = sanFileObject->FsContext;
if (!AFD_START_STATE_CHANGE (sanEndpoint, sanEndpoint->State)) {
goto complete;
}
context = AfdLockEndpointContext (sanEndpoint);
//
// Just make sure that endpoints are of correct type
// and in correct state
//
KeRaiseIrql (DISPATCH_LEVEL, &oldIrql);
AfdAcquireSpinLockAtDpcLevel (&sanEndpoint->SpinLock, &lockHandle);
if (!sanEndpoint->EndpointCleanedUp &&
sanEndpoint->State==AfdEndpointStateConnected) {
//
// Reset the state so we can't get anymore IRPs
//
sanEndpoint->State = AfdEndpointStateTransmitClosing;
//
// Cleanup all IRPs on the endpoint.
//
if (!IsListEmpty (&sanEndpoint->Common.SanEndp.IrpList)) {
PIRP irp;
PDRIVER_CANCEL cancelRoutine;
KIRQL cancelIrql;
AfdReleaseSpinLockFromDpcLevel (&sanEndpoint->SpinLock, &lockHandle);
//
// Acquire cancel spinlock and endpoint spinlock in
// this order and recheck the IRP list
//
IoAcquireCancelSpinLock (&cancelIrql);
ASSERT (cancelIrql==DISPATCH_LEVEL);
AfdAcquireSpinLockAtDpcLevel (&sanEndpoint->SpinLock, &lockHandle);
//
// While list is not empty attempt to cancel the IRPs
//
while (!IsListEmpty (&sanEndpoint->Common.SanEndp.IrpList)) {
irp = CONTAINING_RECORD (
sanEndpoint->Common.SanEndp.IrpList.Flink,
IRP,
Tail.Overlay.ListEntry);
//
// Reset the cancel routine.
//
cancelRoutine = IoSetCancelRoutine (irp, NULL);
if (cancelRoutine!=NULL) {
//
// Cancel routine was not NULL, cancel it here.
// If someone else attempts to complete this IRP
// it will have to wait at least until cancel
// spinlock is released, so we can release
// the endpoint spinlock
//
AfdReleaseSpinLockFromDpcLevel (&sanEndpoint->SpinLock, &lockHandle);
irp->CancelIrql = DISPATCH_LEVEL;
irp->Cancel = TRUE;
(*cancelRoutine) (AfdDeviceObject, irp);
}
else {
IoAcquireCancelSpinLock (&cancelIrql);
ASSERT (cancelIrql==DISPATCH_LEVEL);
AfdAcquireSpinLockAtDpcLevel (&sanEndpoint->SpinLock, &lockHandle);
}
}
IoReleaseCancelSpinLock (DISPATCH_LEVEL);
}
ASSERT (sanEndpoint->Common.SanEndp.SanHlpr!=NULL);
DEREFERENCE_ENDPOINT (sanEndpoint->Common.SanEndp.SanHlpr);
//
// Make sure we cleanup all the fields since they will be
// treated as other part (VC) of the endpoint union.
//
RtlZeroMemory (&sanEndpoint->Common.SanEndp,
sizeof (sanEndpoint->Common.SanEndp));
//
// Reinitialize the endpoint structure.
//
sanEndpoint->Type = AfdBlockTypeEndpoint;
if (sanEndpoint->AddressFileObject!=NULL) {
//
// This is TransmitFile after SuperConnect
//
sanEndpoint->State = AfdEndpointStateBound;
}
else {
//
// This is TransmitFile after SuperAccept
//
sanEndpoint->State = AfdEndpointStateOpen;
}
sanEndpoint->DisconnectMode = 0;
sanEndpoint->EndpointStateFlags = 0;
sanEndpoint->EventsActive = 0;
AfdRecordEndpointsReused ();
status = STATUS_SUCCESS;
}
else {
status = STATUS_INVALID_HANDLE;
}
AfdReleaseSpinLockFromDpcLevel (&sanEndpoint->SpinLock, &lockHandle);
KeLowerIrql (oldIrql);
AfdUnlockEndpointContext (sanEndpoint, context);
AFD_END_STATE_CHANGE (sanEndpoint);
complete:
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanFastRefreshEndpoint, status: 0x%lX", status);
ObDereferenceObject( sanFileObject );
return status;
}
NTSTATUS
AfdSanFastGetPhysicalAddr (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Returns physical address corresponding to provided virtual address.
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_GET_PHYSICAL_ADDR)
RequestorMode - mode of the caller
InputBuffer - user mode virtual address
InputBufferLength - access mode
OutputBuffer - Buffer to place physical address into.
OutputBufferLength - sizeof (PHYSICAL_ADDRESS)
Information - pointer for buffer to place the size of the return information into
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint is of incorrect type
STATUS_INVALID_PARAMETER - invalid access mode.
STATUS_BUFFER_TOO_SMALL - output buffer is of incorrect size.
other - failed when attempting to access input virtual address or output buffer.
--*/
{
#ifndef TEST_RDMA_CACHE
UNREFERENCED_PARAMETER (FileObject);
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (RequestorMode);
UNREFERENCED_PARAMETER (InputBuffer);
UNREFERENCED_PARAMETER (InputBufferLength);
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
UNREFERENCED_PARAMETER (Information);
return STATUS_INVALID_PARAMETER;
#else
NTSTATUS status;
PVOID Va; // virtual address
ULONG accessMode;
PAFD_ENDPOINT sanHlprEndpoint;
PAGED_CODE ();
*Information = 0;
Va = InputBuffer;
accessMode = InputBufferLength;
if (accessMode!=MEM_READ_ACCESS &&
accessMode!=MEM_WRITE_ACCESS) {
return STATUS_INVALID_PARAMETER;
}
if (OutputBufferLength<sizeof(PHYSICAL_ADDRESS)) {
return STATUS_BUFFER_TOO_SMALL;
}
sanHlprEndpoint = FileObject->FsContext;
if (!IS_SAN_HELPER(sanHlprEndpoint) ||
sanHlprEndpoint->OwningProcess!=IoGetCurrentProcess ()) {
return STATUS_INVALID_HANDLE;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if (RequestorMode!=KernelMode) {
//
// Do some verification on the app buffer. Make sure it is
// mapped and that it is a user-mode address with appropriate
// read or write permissions
//
if (accessMode == MEM_READ_ACCESS) {
ProbeAndReadChar ((PCHAR)Va);
}
else {
ProbeForWriteChar ((PCHAR)Va);
}
}
//
// Validate the output structure if it comes from the user mode
// application
//
if (RequestorMode != KernelMode ) {
ASSERT(sizeof(PHYSICAL_ADDRESS) == sizeof(QUAD));
ProbeForWriteQuad ((PQUAD)OutputBuffer);
}
*(PPHYSICAL_ADDRESS)OutputBuffer = MmGetPhysicalAddress(Va);
*Information = sizeof(PHYSICAL_ADDRESS);
status = STATUS_SUCCESS;
} except( AFD_EXCEPTION_FILTER (status) ) {
ASSERT (NT_ERROR (status));
}
UPDATE_ENDPOINT2 (sanHlprEndpoint, "AfdSanGetPhysicalAddress, status: 0x%lX", status);
return status;
#endif // 0
}
NTSTATUS
AfdSanFastGetServicePid (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Returns PID of SAN service process.
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_GET_PHYSICAL_ADDR)
RequestorMode - mode of the caller
InputBuffer - NULL, ignored
InputBufferLength - 0, ignored
OutputBuffer - NULL, ignored
OutputBufferLength - 0, ignored
Information - pointer to buffer to return pid of the SAN service process
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint is of incorrect type
--*/
{
PAFD_ENDPOINT sanHlprEndpoint;
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (RequestorMode);
UNREFERENCED_PARAMETER (InputBuffer);
UNREFERENCED_PARAMETER (InputBufferLength);
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
PAGED_CODE ();
*Information = 0;
sanHlprEndpoint = FileObject->FsContext;
if (!IS_SAN_HELPER(sanHlprEndpoint) ||
sanHlprEndpoint->OwningProcess!=IoGetCurrentProcess ()) {
return STATUS_INVALID_HANDLE;
}
*Information = (ULONG_PTR)AfdSanServicePid;
UPDATE_ENDPOINT2 (sanHlprEndpoint,
"AfdSanFastGetServicePid, pid: 0x%lX",
HandleToUlong (AfdSanServicePid));
return STATUS_SUCCESS;
}
NTSTATUS
AfdSanFastSetServiceProcess (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Set the service helper endpoint
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_GET_PHYSICAL_ADDR)
RequestorMode - mode of the caller
InputBuffer - NULL, ignored
InputBufferLength - 0, ignored
OutputBuffer - NULL, ignored
OutputBufferLength - 0, ignored
Information - 0, ignored
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint is of incorrect type
STATUS_ACCESS_DENIED - process is not priviliged enough to become service process.
STATUS_ADDRESS_ALREADY_EXISTS - service process has already registered.
--*/
{
NTSTATUS status;
PAFD_ENDPOINT sanHlprEndpoint;
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (RequestorMode);
UNREFERENCED_PARAMETER (InputBuffer);
UNREFERENCED_PARAMETER (InputBufferLength);
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
PAGED_CODE ();
*Information = 0;
sanHlprEndpoint = FileObject->FsContext;
if (!IS_SAN_HELPER(sanHlprEndpoint) ||
sanHlprEndpoint->OwningProcess!=IoGetCurrentProcess ()) {
return STATUS_INVALID_HANDLE;
}
if (!sanHlprEndpoint->AdminAccessGranted) {
return STATUS_ACCESS_DENIED;
}
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
if (AfdSanServiceHelper==NULL) {
AfdSanServiceHelper = sanHlprEndpoint;
AfdSanServicePid = PsGetCurrentProcessId ();
status = STATUS_SUCCESS;
}
else if (AfdSanServiceHelper==sanHlprEndpoint) {
ASSERT (FileObject->LockOperation == TRUE);
status = STATUS_SUCCESS;
}
else {
status = STATUS_ADDRESS_ALREADY_EXISTS;
}
ExReleaseResourceLite (AfdResource);
KeLeaveCriticalRegion ();
UPDATE_ENDPOINT2 (sanHlprEndpoint, "AfdSanFastSetServiceProcess, status: 0x%lX", status);
return status;
}
NTSTATUS
AfdSanFastProviderChange (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Notifies interested processes of SAN provider addition/deletion/change
Arguments:
FileObject - SAN helper object for the service process communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_PROVIDER_CHANGE)
RequestorMode - mode of the caller
InputBuffer - NULL, ignored
InputBufferLength - 0, ignored
OutputBuffer - NULL, ignored
OutputBufferLength - 0, ignored
Information - 0, ignored
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint is of incorrect type
STATUS_ACCESS_DENIED - helper endpoint is not for the service process.
--*/
{
PAFD_ENDPOINT sanHlprEndpoint;
UNREFERENCED_PARAMETER (IoctlCode);
UNREFERENCED_PARAMETER (RequestorMode);
UNREFERENCED_PARAMETER (InputBuffer);
UNREFERENCED_PARAMETER (InputBufferLength);
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
*Information = 0;
sanHlprEndpoint = FileObject->FsContext;
if (!IS_SAN_HELPER(sanHlprEndpoint) ||
sanHlprEndpoint->OwningProcess!=IoGetCurrentProcess ()) {
return STATUS_INVALID_HANDLE;
}
if (sanHlprEndpoint!=AfdSanServiceHelper) {
return STATUS_ACCESS_DENIED;
}
UPDATE_ENDPOINT2 (sanHlprEndpoint,
"AfdSanFastProviderChange, seq num: 0x%lX",
AfdSanProviderListSeqNum);
AfdSanProcessAddrListForProviderChange (NULL);
return STATUS_SUCCESS;
}
NTSTATUS
FASTCALL
AfdSanAddrListChange (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Processes address SAN list change IRP
Notifies both of address list changes and SAN
provider changes.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the stack location to use for this request.
Return Value:
NTSTATUS -- Indicates whether the request was successfully queued.
--*/
{
PAFD_ENDPOINT sanHlprEndpoint;
NTSTATUS status;
sanHlprEndpoint = IrpSp->FileObject->FsContext;
if (IS_SAN_HELPER(sanHlprEndpoint) &&
sanHlprEndpoint->OwningProcess==IoGetCurrentProcess ()) {
if (AfdSanProviderListSeqNum==0 ||
sanHlprEndpoint->Common.SanHlpr.Plsn==AfdSanProviderListSeqNum) {
status = AfdAddressListChange (Irp, IrpSp);
if (AfdSanProviderListSeqNum==0 ||
sanHlprEndpoint->Common.SanHlpr.Plsn==AfdSanProviderListSeqNum) {
UPDATE_ENDPOINT (sanHlprEndpoint);
}
else {
AfdSanProcessAddrListForProviderChange (sanHlprEndpoint);
}
return status;
}
else {
sanHlprEndpoint->Common.SanHlpr.Plsn = AfdSanProviderListSeqNum;
status = STATUS_SUCCESS;
Irp->IoStatus.Information = sanHlprEndpoint->Common.SanHlpr.Plsn;
UPDATE_ENDPOINT2 (sanHlprEndpoint,
"AfdSanAddrListChange, new plsn: 0x%lX",
sanHlprEndpoint->Common.SanHlpr.Plsn);
}
}
else {
status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information = 0;
UPDATE_ENDPOINT2 (sanHlprEndpoint, "AfdSanAddrListChange invalid helper: 0x%lX", status);
}
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, AfdPriorityBoost);
return status;
}
NTSTATUS
FASTCALL
AfdSanAcquireContext (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Requests transfer of the socket context to the current process.
Arguments:
Irp - acquire conect IRP
IrpSp - current stack location
Return Value:
STATUS_PENDING - operation was successfully enqued
--*/
{
NTSTATUS status;
AFD_SWITCH_ACQUIRE_CTX_INFO ctxInfo;
PAFD_ENDPOINT sanHlprEndpoint, sanEndpoint;
PFILE_OBJECT sanFileObject = NULL;
PVOID requestCtx;
AFD_LOCK_QUEUE_HANDLE lockHandle;
BOOLEAN doTransfer;
PVOID context;
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_SWITCH_ACQUIRE_CTX_INFO ctxInfo32;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (*ctxInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (Irp->RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof (*ctxInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_ACQUIRE_CTX_INFO32));
}
ctxInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
ctxInfo.SocketHandle = ctxInfo32->SocketHandle;
ctxInfo.SwitchContext = ctxInfo32->SwitchContext;
ctxInfo.SocketCtxBuf = ctxInfo32->SocketCtxBuf;
ctxInfo.SocketCtxBufSize = ctxInfo32->SocketCtxBufSize;
}
else
#endif //_WIN64
{
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<sizeof (ctxInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (Irp->RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
sizeof (ctxInfo),
PROBE_ALIGNMENT (AFD_SWITCH_ACQUIRE_CTX_INFO));
}
ctxInfo = *((PAFD_SWITCH_ACQUIRE_CTX_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
}
if (ctxInfo.SocketCtxBufSize < 1)
ExRaiseStatus (STATUS_INVALID_PARAMETER);
Irp->MdlAddress = IoAllocateMdl (ctxInfo.SocketCtxBuf, // VirtualAddress
ctxInfo.SocketCtxBufSize, // Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL); // Irp
if (Irp->MdlAddress==NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
MmProbeAndLockPages(
Irp->MdlAddress, // MemoryDescriptorList
Irp->RequestorMode, // AccessMode
IoWriteAccess // Operation
);
if (MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority)==NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>0) {
Irp->MdlAddress->Next = IoAllocateMdl (Irp->UserBuffer, // VirtualAddress
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,// Length
FALSE, // SecondaryBuffer
TRUE, // ChargeQuota
NULL); // Irp
if (Irp->MdlAddress->Next==NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
MmProbeAndLockPages(
Irp->MdlAddress->Next, // MemoryDescriptorList
Irp->RequestorMode, // AccessMode
IoWriteAccess // Operation
);
if (MmGetSystemAddressForMdlSafe(Irp->MdlAddress->Next, LowPagePriority)==NULL) {
ExRaiseStatus (STATUS_INSUFFICIENT_RESOURCES);
}
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
//
// Cleanup partially processed MDLs since IO subsystem can't do it.
//
while (Irp->MdlAddress!=NULL) {
PMDL mdl = Irp->MdlAddress;
Irp->MdlAddress = mdl->Next;
mdl->Next = NULL;
if (mdl->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (mdl);
}
IoFreeMdl (mdl);
}
goto complete;
}
status = ObReferenceObjectByHandle (
ctxInfo.SocketHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14)&3,
*IoFileObjectType,
Irp->RequestorMode,
(PVOID)&sanFileObject,
NULL
);
if (!NT_SUCCESS (status)) {
goto complete;
}
if (sanFileObject->DeviceObject!=AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
sanHlprEndpoint = IrpSp->FileObject->FsContext;
sanEndpoint = sanFileObject->FsContext;
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanFastAcquireCtx: endp-%p.\n",
sanEndpoint));
}
//
// Just make sure that endpoints are of correct type
//
context = AfdLockEndpointContext (sanEndpoint); // To prevent refresh.
if (IS_SAN_HELPER(sanHlprEndpoint) &&
sanHlprEndpoint->OwningProcess==IoGetCurrentProcess () &&
IS_SAN_ENDPOINT(sanEndpoint)) {
if (sanEndpoint->Common.SanEndp.SanHlpr==AfdSanServiceHelper &&
sanHlprEndpoint==AfdSanServiceHelper) {
//
// This is an implicit duplication request from the service process.
// (we already received all the data from the owning process and
// associated sanEndpoint with the service process).
//
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
//
// Make sure endpoint is still in transferring state
//
if (sanEndpoint->Common.SanEndp.CtxTransferStatus!=STATUS_MORE_PROCESSING_REQUIRED) {
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
status = STATUS_CANCELLED;
}
else if (ctxInfo.SocketCtxBufSize > sanEndpoint->Common.SanEndp.SavedContextLength ||
ctxInfo.SocketCtxBufSize +
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sanEndpoint->Common.SanEndp.SavedContextLength) {
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
//
// Switch should have queried the context size via AFD_GET_CONTEXT
//
status = STATUS_BUFFER_TOO_SMALL;
}
else {
PVOID savedContext = sanEndpoint->Common.SanEndp.SavedContext;
ULONG savedContextLength = sanEndpoint->Common.SanEndp.SavedContextLength;
sanEndpoint->Common.SanEndp.SavedContext = NULL;
sanEndpoint->Common.SanEndp.SavedContextLength = 0;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
RtlCopyMemory (
MmGetSystemAddressForMdl (Irp->MdlAddress),
savedContext,
ctxInfo.SocketCtxBufSize);
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0) {
Irp->IoStatus.Information =
savedContextLength-
ctxInfo.SocketCtxBufSize;
RtlCopyMemory (
MmGetSystemAddressForMdl (Irp->MdlAddress->Next),
(PCHAR)savedContext+ctxInfo.SocketCtxBufSize,
Irp->IoStatus.Information);
}
else {
Irp->IoStatus.Information = 0;
}
AFD_FREE_POOL (savedContext, AFD_SAN_CONTEXT_POOL_TAG);
// sanEndpoint->Common.SanEndp.SavedContext = NULL;
sanEndpoint->Common.SanEndp.SwitchContext = ctxInfo.SwitchContext;
status = STATUS_SUCCESS;
//
// Note that we are not expecting a reply from service process
// anymore.
//
InterlockedExchangeAdd (&AfdSanServiceHelper->Common.SanHlpr.PendingRequests, -2);
}
AfdUnlockEndpointContext (sanEndpoint, context);
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdSanAcquireContext, ctx bytes copied/status: 0x%lX",
NT_SUCCESS (status)
? (ULONG)Irp->IoStatus.Information
: status);
//
// Complete the IRP
//
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, AfdPriorityBoost);
//
// Restart request processing.
//
AfdSanRestartRequestProcessing (sanEndpoint, status);
}
else {
AfdAcquireSpinLock (&sanEndpoint->SpinLock, &lockHandle);
//
// Make sure Irp has not been cancelled meanwhile
//
IoSetCancelRoutine (Irp, AfdSanCancelRequest);
if (Irp->Cancel) {
//
// Oops, let it go
//
Irp->Tail.Overlay.ListEntry.Flink = NULL;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
if (IoSetCancelRoutine (Irp, NULL)==NULL) {
KIRQL cancelIrql;
//
// Cancel routine must be running, make sure
// it completes before we complete the IRP
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
AfdUnlockEndpointContext (sanEndpoint, context);
status = STATUS_CANCELLED;
goto complete;
}
//
// Check if don't already have duplication in progress.
//
if (sanEndpoint->Common.SanEndp.CtxTransferStatus!=STATUS_PENDING &&
sanEndpoint->Common.SanEndp.CtxTransferStatus!=STATUS_MORE_PROCESSING_REQUIRED) {
if (!NT_SUCCESS (sanEndpoint->Common.SanEndp.CtxTransferStatus)) {
//
// Duplicaiton has failed previously, can't do another one.
//
status = sanEndpoint->Common.SanEndp.CtxTransferStatus;
Irp->Tail.Overlay.ListEntry.Flink = NULL;
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
if (IoSetCancelRoutine (Irp, NULL)==NULL) {
KIRQL cancelIrql;
//
// Cancel routine must be running, make sure
// it completes before we complete the IRP
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
AfdUnlockEndpointContext (sanEndpoint, context);
goto complete;
}
//
// Generate request context that uniquely identifies
// it among other requests on the same endpoint.
//
requestCtx = AFD_SWITCH_MAKE_REQUEST_CONTEXT(
sanEndpoint->Common.SanEndp.RequestId,
AFD_SWITCH_REQUEST_TFCTX);
sanEndpoint->Common.SanEndp.RequestId += 1;
//
// Store the request context in the Irp and insert it into
// the list
//
Irp->AfdSanRequestInfo.AfdSanRequestCtx = requestCtx;
sanEndpoint->Common.SanEndp.CtxTransferStatus = STATUS_PENDING;
doTransfer = TRUE;
}
else {
//
// Another duplication in progress, this one will have to wait.
//
Irp->AfdSanRequestInfo.AfdSanRequestCtx = NULL;
doTransfer = FALSE;
AFD_W4_INIT requestCtx = NULL; // Depends on variable above, but
// compiler does not see
// the connection.
}
Irp->AfdSanRequestInfo.AfdSanSwitchCtx = ctxInfo.SwitchContext;
Irp->AfdSanRequestInfo.AfdSanProcessId = PsGetCurrentProcessId();
Irp->AfdSanRequestInfo.AfdSanHelperEndp = sanHlprEndpoint;
//
// We are going to pend this IRP, mark it so
//
IoMarkIrpPending (Irp);
InsertTailList (&sanEndpoint->Common.SanEndp.IrpList,
&Irp->Tail.Overlay.ListEntry);
IoGetCurrentIrpStackLocation(Irp)->FileObject = sanFileObject;
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdSanAcquireContext, request: 0x%lX",
PtrToUlong (Irp->AfdSanRequestInfo.AfdSanRequestCtx));
AfdReleaseSpinLock (&sanEndpoint->SpinLock, &lockHandle);
AfdUnlockEndpointContext (sanEndpoint, context);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanRedirectRequest: endp-%p, irp-%p, context-%p\n",
sanEndpoint, Irp, requestCtx));
}
if (doTransfer) {
status = AfdSanNotifyRequest (sanEndpoint,
requestCtx,
STATUS_SUCCESS,
(ULONG_PTR)PsGetCurrentProcessId());
if (!NT_SUCCESS (status)) {
PIRP irp;
//
// If notification failed, fail the request.
// Note that we cannot return the failure status directly
// as we already marked IRP as pending. Also, the IRP
// could have been cancelled, so we have to search for
// it in the list.
//
irp = AfdSanDequeueRequest (sanEndpoint, requestCtx);
if (irp!=NULL) {
ASSERT (irp==Irp);
irp->IoStatus.Status = status;
IoCompleteRequest (irp, AfdPriorityBoost);
}
//
// Restart other requests in the queue and reset context
// transfer status.
//
AfdSanRestartRequestProcessing (sanEndpoint, STATUS_SUCCESS);
}
}
//
// The request is in the queue or completed, we no longer need
// object reference.
//
status = STATUS_PENDING;
}
ObDereferenceObject (sanFileObject);
return status;
}
else {
AfdUnlockEndpointContext (sanEndpoint, context);
status = STATUS_INVALID_HANDLE;
}
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanAcquireContext, status: 0x%lX", status);
complete:
if (sanFileObject!=NULL)
ObDereferenceObject (sanFileObject);
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, AfdPriorityBoost);
return status;
}
NTSTATUS
AfdSanFastTransferCtx (
IN PFILE_OBJECT FileObject,
IN ULONG IoctlCode,
IN KPROCESSOR_MODE RequestorMode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG_PTR Information
)
/*++
Routine Description:
Requests AFD to transfer endpoint into another process context
Arguments:
FileObject - SAN helper object - communication channel between the
switch and AFD in the process.
IoctlCode - operation IOCTL code (IOCTL_AFD_SWITCH_TRANSFER_CTX)
RequestorMode - mode of the caller
InputBuffer - input parameters for the operation (AFD_SWITCH_TRANSFER_CTX_INFO)
SocketHandle - SAN endpoint to be transferred
RequestContext - value that identifies corresponding acquire request.
SocketCtxBuf - endpoint context buffer to copy destination process
acquire request
SocketCtxSize - size of the buffer to copy
RcvBufferArray - array of buffered data to transfer to
destination process acquire request
RcvBufferCount - number of elements in the array.
InputBufferLength - sizeof (AFD_SWITCH_TRANSFER_CTX_INFO)
OutputBuffer - unused
OutputBufferLength - unused
Information - pointer to buffer to return number of bytes copied
Return Value:
STATUS_SUCCESS - operation succeeded
STATUS_INVALID_HANDLE - helper endpoint or switch endpoint is of incorrect type
STATUS_INVALID_PARAMETER - invalid input buffer size.
other - failed when attempting to access san endpoint or input buffer(s).
--*/
{
NTSTATUS status;
AFD_SWITCH_TRANSFER_CTX_INFO ctxInfo;
PVOID context;
PAFD_ENDPOINT sanHlprEndpoint, sanEndpoint;
PFILE_OBJECT sanFileObject;
PIRP irp;
PIO_STACK_LOCATION irpSp;
#ifdef _WIN64
WSABUF localArray[8];
LPWSABUF pArray = localArray;
#endif
PAGED_CODE ();
*Information = 0;
UNREFERENCED_PARAMETER (OutputBuffer);
UNREFERENCED_PARAMETER (OutputBufferLength);
AFD_W4_INIT status = STATUS_SUCCESS;
try {
#ifdef _WIN64
if (IoIs32bitProcess (NULL)) {
PAFD_SWITCH_TRANSFER_CTX_INFO ctxInfo32;
ULONG i;
if (InputBufferLength<sizeof (*ctxInfo32)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (*ctxInfo32),
PROBE_ALIGNMENT32 (AFD_SWITCH_TRANSFER_CTX_INFO32));
}
ctxInfo32 = InputBuffer;
ctxInfo.SocketHandle = ctxInfo32->SocketHandle;
ctxInfo.SwitchContext = ctxInfo32->SwitchContext;
ctxInfo.RequestContext = ctxInfo32->RequestContext;
ctxInfo.SocketCtxBuf = ctxInfo32->SocketCtxBuf;
ctxInfo.SocketCtxBufSize = ctxInfo32->SocketCtxBufSize;
ctxInfo.RcvBufferArray = ctxInfo32->RcvBufferArray;
ctxInfo.RcvBufferCount = ctxInfo32->RcvBufferCount;
ctxInfo.Status = ctxInfo32->Status;
if (RequestorMode!=KernelMode) {
if (ctxInfo.SocketCtxBufSize==0 ||
ctxInfo.RcvBufferCount>(MAXULONG/sizeof (WSABUF32))) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
ProbeForRead (ctxInfo.SocketCtxBuf,
ctxInfo.SocketCtxBufSize,
sizeof (UCHAR));
ProbeForRead (ctxInfo.RcvBufferArray,
sizeof (WSABUF32)*ctxInfo.RcvBufferCount,
PROBE_ALIGNMENT32 (WSABUF32));
}
if (ctxInfo.RcvBufferCount>sizeof(localArray)/sizeof(localArray[0])) {
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
NonPagedPool,
sizeof (WSABUF)*ctxInfo.RcvBufferCount,
AFD_TEMPORARY_POOL_TAG);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT (pArray!=NULL);
}
for (i=0; i<ctxInfo.RcvBufferCount; i++) {
pArray[i].buf = ctxInfo.RcvBufferArray[i].buf;
pArray[i].len = ctxInfo.RcvBufferArray[i].len;
}
ctxInfo.RcvBufferArray = pArray;
}
else
#endif //_WIN64
{
if (InputBufferLength<sizeof (ctxInfo)) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (RequestorMode!=KernelMode) {
ProbeForReadSmallStructure (InputBuffer,
sizeof (ctxInfo),
PROBE_ALIGNMENT (AFD_SWITCH_TRANSFER_CTX_INFO));
}
ctxInfo = *((PAFD_SWITCH_TRANSFER_CTX_INFO)InputBuffer);
if (RequestorMode!=KernelMode) {
if (ctxInfo.SocketCtxBufSize==0 ||
ctxInfo.RcvBufferCount>(MAXULONG/sizeof (WSABUF))) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
ProbeForRead (ctxInfo.SocketCtxBuf,
ctxInfo.SocketCtxBufSize,
sizeof (UCHAR));
ProbeForRead (ctxInfo.RcvBufferArray,
sizeof (ctxInfo.RcvBufferArray)*ctxInfo.RcvBufferCount,
PROBE_ALIGNMENT (WSABUF));
}
}
if (ctxInfo.SwitchContext==NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AFD: Switch context is NULL in AfdSanFastTransferCtx\n"));
}
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
ctxInfo.Status = AfdValidateStatus (ctxInfo.Status);
sanHlprEndpoint = FileObject->FsContext;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
status = AfdSanReferenceSwitchSocketByHandle (
ctxInfo.SocketHandle,
(IoctlCode>>14)&3,
RequestorMode,
sanHlprEndpoint,
ctxInfo.SwitchContext,
&sanFileObject
);
if (!NT_SUCCESS (status)) {
goto complete;
}
sanEndpoint = sanFileObject->FsContext;
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanFastTransferCtx: endp-%p.\n",
sanEndpoint));
}
if (ctxInfo.RequestContext==AFD_SWITCH_MAKE_REQUEST_CONTEXT (0, AFD_SWITCH_REQUEST_TFCTX)) {
//
// This is unsolicited request to transfer endpoint into
// the service process.
//
PVOID savedContext = NULL;
ULONG ctxLength;
if (NT_SUCCESS (ctxInfo.Status)) {
//
// Save the user mode data.
//
ctxLength = ctxInfo.SocketCtxBufSize;
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
if (ctxInfo.RcvBufferCount>0)
ctxLength += AfdCalcBufferArrayByteLength(
ctxInfo.RcvBufferArray,
ctxInfo.RcvBufferCount);
savedContext = AFD_ALLOCATE_POOL_WITH_QUOTA (PagedPool,
ctxLength,
AFD_SAN_CONTEXT_POOL_TAG);
ASSERT (savedContext!=NULL);
RtlCopyMemory (
savedContext,
ctxInfo.SocketCtxBuf,
ctxInfo.SocketCtxBufSize);
if (ctxInfo.RcvBufferCount>0) {
AfdCopyBufferArrayToBuffer (
(PUCHAR)savedContext+ctxInfo.SocketCtxBufSize,
ctxLength-ctxInfo.SocketCtxBufSize,
ctxInfo.RcvBufferArray,
ctxInfo.RcvBufferCount);
}
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto CleanupSavedContext;
}
status = AfdSanDupEndpointIntoServiceProcess (sanFileObject, savedContext, ctxLength);
if (NT_SUCCESS (status)) {
*Information = ctxLength;
goto complete_deref;
}
CleanupSavedContext:
//
// Something failed, free the context
//
if (savedContext!=NULL) {
AFD_FREE_POOL (savedContext, AFD_SAN_CONTEXT_POOL_TAG);
}
}
else {
//
// The process could not satisfy implicit transfer context request
//
status = ctxInfo.Status;
}
}
else {
//
// The process satisfied another process acquire request
// Find it first.
//
irp = AfdSanDequeueRequest (sanEndpoint, ctxInfo.RequestContext);
if (irp!=NULL) {
//
// Get IRP stack location and perform data copy
//
irpSp = IoGetCurrentIrpStackLocation (irp);
if (NT_SUCCESS (ctxInfo.Status)) {
AFD_SWITCH_CONTEXT localContext;
//
// Set the initial status to success since the process
// agreed to satisfy the transfer.
//
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
if ( MmGetMdlByteCount(irp->MdlAddress)!=ctxInfo.SocketCtxBufSize ||
(ctxInfo.RcvBufferCount!=0 &&
irpSp->Parameters.DeviceIoControl.OutputBufferLength<
AfdCalcBufferArrayByteLength(
ctxInfo.RcvBufferArray,
ctxInfo.RcvBufferCount)) ){
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
RtlCopyMemory (
MmGetSystemAddressForMdl (irp->MdlAddress),
ctxInfo.SocketCtxBuf,
ctxInfo.SocketCtxBufSize);
if (ctxInfo.RcvBufferCount>0) {
irp->IoStatus.Information =
AfdCopyBufferArrayToBuffer (
MmGetSystemAddressForMdl (irp->MdlAddress->Next),
irpSp->Parameters.DeviceIoControl.OutputBufferLength,
ctxInfo.RcvBufferArray,
ctxInfo.RcvBufferCount);
}
else {
irp->IoStatus.Information = 0;
}
}
except (AFD_EXCEPTION_FILTER (status)) {
goto CopyException;
}
//
// Now change sanEndpoint's SanHlpr and SwitchContext to point
// to the new address space and switch socket
//
context = AfdLockEndpointContext (sanEndpoint);
if (!IS_SAN_ENDPOINT (sanEndpoint ) ||
sanEndpoint->Common.SanEndp.CtxTransferStatus!=STATUS_PENDING) {
status = STATUS_INVALID_HANDLE;
goto UnlockContext;
}
try {
localContext = *sanEndpoint->Common.SanEndp.SwitchContext;
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto UnlockContext;
}
KeAttachProcess (PsGetProcessPcb(((PAFD_ENDPOINT)irp->AfdSanRequestInfo.AfdSanHelperEndp)->OwningProcess));
try {
//
// Place info regarding select/eventselect events in SwitchContext
// of the new switch socket
//
*((PAFD_SWITCH_CONTEXT)irp->AfdSanRequestInfo.AfdSanSwitchCtx) = localContext;
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
KeDetachProcess ();
goto UnlockContext;
}
KeDetachProcess ();
sanEndpoint->Common.SanEndp.SanHlpr = irp->AfdSanRequestInfo.AfdSanHelperEndp;
REFERENCE_ENDPOINT2 (sanEndpoint->Common.SanEndp.SanHlpr,
"Transfer TO 0x%lX",
HandleToUlong (ctxInfo.SocketHandle) );
sanEndpoint->Common.SanEndp.SwitchContext = irp->AfdSanRequestInfo.AfdSanSwitchCtx;
//
// Reset implicit dup flag if it was set.
// We are satisfying explicit request for
// duplication.
//
sanEndpoint->Common.SanEndp.ImplicitDup = FALSE;
DEREFERENCE_ENDPOINT2 (sanHlprEndpoint,
"Transfer FROM 0x%lX",
HandleToUlong (ctxInfo.SocketHandle));
UnlockContext:
AfdUnlockEndpointContext (sanEndpoint, context);
CopyException:
//
// Report the final status to the process
// that wanted duplication
//
irp->IoStatus.Status = status;
}
else {
//
// This process could not satisfy transfer request
// Tell other process about this.
//
irp->IoStatus.Status = ctxInfo.Status;
//
// We succeeded this process refuse to dup request
// we should report it as success and continue processing
// of other redirected request. It is possible that we
// can still succeed other duplication requests.
//
status = STATUS_SUCCESS;
}
//
// Complete the acquuire request from another process.
//
IoCompleteRequest (irp, AfdPriorityBoost);
}
else {
status = STATUS_INVALID_PARAMETER;
}
}
//
// Restart request processing that were pended while
// we were processing duplication request.
//
AfdSanRestartRequestProcessing (sanEndpoint, status);
complete_deref:
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanFastTransferCtx, status: 0x%lX", status);
ObDereferenceObject (sanFileObject);
complete:
#ifdef _WIN64
if (pArray!=localArray) {
AFD_FREE_POOL (pArray, AFD_TEMPORARY_POOL_TAG);
}
#endif //_WIN64
return status;
}
BOOLEAN
AfdSanFastUnlockAll (
IN PFILE_OBJECT FileObject,
IN PEPROCESS Process,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Called by the system when last handle to the file object in closed
in some process while other processes still have handles opened
This is only called on files that were marked as locked at some point of time.
Arguments:
FileObject - file object of interest
Process - process which has last handle being closed
IoStatus - buffer to return operation status and information
DeviceObject - device object with which file object is associated
Return Value:
TRUE - operation completed OK.
--*/
{
PAFD_ENDPOINT sanEndpoint;
PVOID context;
PAGED_CODE ();
UNREFERENCED_PARAMETER (DeviceObject);
PAGED_CODE ();
sanEndpoint = FileObject->FsContext;
context = AfdLockEndpointContext (sanEndpoint);
if (IS_SAN_ENDPOINT (sanEndpoint)) {
ASSERT (sanEndpoint->Common.SanEndp.SanHlpr!=NULL);
if (sanEndpoint->Common.SanEndp.SanHlpr->OwningProcess==Process) {
NTSTATUS status = STATUS_CANCELLED;
//
// Owner process is closing it.
//
if (sanEndpoint->Common.SanEndp.SanHlpr!=AfdSanServiceHelper) {
NTSTATUS dupStatus;
dupStatus = AfdSanSetAskDupeToServiceState (sanEndpoint);
if (dupStatus == STATUS_PENDING ||
dupStatus == STATUS_MORE_PROCESSING_REQUIRED) {
//
// Some other process is already trying to import this socket. Let
// that complete
//
AfdUnlockEndpointContext (sanEndpoint, context);
goto Exit;
}
if (NT_SUCCESS (dupStatus)) {
PAFD_ENDPOINT sanHlprEndpoint = sanEndpoint->Common.SanEndp.SanHlpr;
//
// Last handle in the process that owns the socket is being
// closed while there are handles to it in other processes.
// Need to transfer the context to the service process
//
if ((InterlockedExchangeAdd (&sanHlprEndpoint->Common.SanHlpr.PendingRequests, 2) & 1)==0) {
status = IoSetIoCompletion (
sanHlprEndpoint->Common.SanHlpr.IoCompletionPort,
sanEndpoint->Common.SanEndp.SwitchContext,
AFD_SWITCH_MAKE_REQUEST_CONTEXT (0, AFD_SWITCH_REQUEST_TFCTX),
STATUS_SUCCESS,
(ULONG_PTR)AfdSanServicePid,
FALSE // ChargeQuota - Don't, handle is going away, no
// way for the run-away app to mount an attack
);
UPDATE_ENDPOINT2 (sanEndpoint, "Implicit TFCTX request, status: 0x%lX", status);
if (NT_SUCCESS (status)) {
AfdUnlockEndpointContext (sanEndpoint, context);
goto Exit;
}
}
else {
//
// Process has already exited, not much we can do.
//
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AFD: Process %p has exited before SAN context could be transferred out.\n",
Process));
}
InterlockedExchangeAdd (&sanHlprEndpoint->Common.SanHlpr.PendingRequests, -2);
}
}
AfdUnlockEndpointContext (sanEndpoint, context);
AfdSanRestartRequestProcessing (sanEndpoint, status);
}
else {
if (sanEndpoint->Common.SanEndp.ImplicitDup) {
AfdUnlockEndpointContext (sanEndpoint, context);
//
// Process is exiting and the only handle is in the
// other process where the handle was duplicated
// impilictly (without application request, e.g.
// cross proceess accepting socket duplicated into
// listening process to complete the accept or socket
// duplication into service process while waiting on
// other process to request ownership).
//
if (ObReferenceObject (FileObject)==3) {
NTSTATUS oldStatus;
// Why are we checking for refcount of 3?
// 1 - Handle in the implicit process
// 1 - IO manager for this call
// 1 - we just added
oldStatus = AfdSanRestartRequestProcessing (sanEndpoint, STATUS_CANCELLED);
if (NT_SUCCESS (oldStatus)) {
ASSERT (oldStatus!=STATUS_PENDING);
ASSERT ((ULONG_PTR)sanEndpoint->Common.SanEndp.SwitchContext<MM_USER_PROBE_ADDRESS);
//
// Notify the service process only if duplication has already
// completed
//
UPDATE_ENDPOINT2 (sanEndpoint, "AfdSanFastUnlockAll, posting CLSOCK", 0);
IoSetIoCompletion (
sanEndpoint->Common.SanEndp.SanHlpr->Common.SanHlpr.IoCompletionPort,
sanEndpoint->Common.SanEndp.SwitchContext,
AFD_SWITCH_MAKE_REQUEST_CONTEXT (0, AFD_SWITCH_REQUEST_CLSOC),
STATUS_SUCCESS,
(ULONG_PTR)0,
FALSE); // ChargeQuota - Don't, handle is going away, no
// way for the run-away app to mount an attack
}
}
ObDereferenceObject (FileObject);
}
else {
AfdUnlockEndpointContext (sanEndpoint, context);
}
}
}
else if (IS_SAN_HELPER (sanEndpoint)) {
//
// Helper never change to it safe to release the lock.
//
AfdUnlockEndpointContext (sanEndpoint, context);
//
// The process for which helper was created is exiting
// we need to cleanup all the endpoint that refer to this helper.
//
if (sanEndpoint->OwningProcess==Process) {
AfdSanHelperCleanup (sanEndpoint);
}
}
else {
//
// non-SAN endpoint. Someone else called NtLockFile on endpoint
// file object ...
//
AfdUnlockEndpointContext (sanEndpoint, context);
}
Exit:
IoStatus->Status = STATUS_SUCCESS;
IoStatus->Information = 0;
return TRUE;
}
//
// Internal routines.
//
NTSTATUS
AfdSanAcceptCore (
PIRP AcceptIrp,
PFILE_OBJECT AcceptFileObject,
PAFD_CONNECTION Connection,
PAFD_LOCK_QUEUE_HANDLE LockHandle
)
/*++
Routine Description:
Accept the incoming SAN connection on endpoint provided
Arguments:
AcceptIrp - accept IRP to complete
AcceptFileObject - file object of accepting endpoint
Connection - pointer to connection object that represents the incoming connection
LockHandle - IRQL at which listening endpoint spinlock was taken on entry to this routine.
Return Value:
STATUS_PENDING - accept operation started OK
STATUS_REMOTE_DISCONNECT - connection being accepted was aborted by the remote.
STATUS_CANCELLED - accepting endpoint was closed or accept IRP cancelled
--*/
{
PIRP connectIrp;
PIO_STACK_LOCATION irpSp;
PAFD_SWITCH_CONNECT_INFO connectInfo;
PAFD_ENDPOINT listenEndpoint, acceptEndpoint;
HANDLE acceptHandle;
ULONG receiveLength;
PKPROCESS listenProcess;
ASSERT (LockHandle->LockHandle.OldIrql < DISPATCH_LEVEL);
irpSp = IoGetCurrentIrpStackLocation (AcceptIrp);
listenEndpoint = irpSp->FileObject->FsContext;
acceptEndpoint = AcceptFileObject->FsContext;
ASSERT (Connection->SanConnection);
//
// Snag the connect indication IRP
//
connectIrp = Connection->ConnectIrp;
ASSERT (connectIrp!=NULL);
Connection->ConnectIrp = NULL;
//
// Handle EventSelect signalling
//
listenEndpoint->EventsActive &= ~AFD_POLL_ACCEPT;
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanAcceptCore: Endp %p, Active %lx\n",
listenEndpoint,
listenEndpoint->EventsActive
));
}
if (!IsListEmpty (&listenEndpoint->Common.VcListening.UnacceptedConnectionListHead ) ) {
AfdIndicateEventSelectEvent(
listenEndpoint,
AFD_POLL_ACCEPT,
STATUS_SUCCESS
);
}
//
// We no longer need the connection object for SAN
// endpoint, return it
//
Connection->Endpoint = NULL;
Connection->SanConnection = FALSE;
AfdSanReleaseConnection (listenEndpoint, Connection, FALSE);
DEREFERENCE_ENDPOINT (listenEndpoint);
if (IoSetCancelRoutine (connectIrp, NULL)==NULL) {
AfdReleaseSpinLock (&listenEndpoint->SpinLock, LockHandle);
connectIrp->IoStatus.Status = STATUS_CANCELLED;
connectIrp->IoStatus.Information = 0;
IoCompleteRequest (connectIrp, AfdPriorityBoost);
return STATUS_REMOTE_DISCONNECT;
}
AfdReleaseSpinLock (&listenEndpoint->SpinLock, LockHandle);
//
// Check that accept IRP and SAN connection IRP belong to the same process
//
if (IoThreadToProcess (AcceptIrp->Tail.Overlay.Thread)!=
IoThreadToProcess (connectIrp->Tail.Overlay.Thread)) {
//
// Listen process is different than the accepting one.
// We need to duplicate accepting handle into the listening
// process so that accept can take place there and the accepting
// socket will later get dup-ed into the accepting process when
// that process performs an IO operation on it.
//
NTSTATUS status;
listenProcess = PsGetProcessPcb(IoThreadToProcess (connectIrp->Tail.Overlay.Thread));
KeAttachProcess (listenProcess);
status = ObOpenObjectByPointer (
AcceptFileObject,
OBJ_CASE_INSENSITIVE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
KernelMode,
&acceptHandle);
KeDetachProcess ();
if (!NT_SUCCESS (status)) {
connectIrp->IoStatus.Status = STATUS_INVALID_HANDLE;
connectIrp->IoStatus.Information = 0;
IoCompleteRequest (connectIrp, AfdPriorityBoost);
return STATUS_REMOTE_DISCONNECT;
}
}
else {
acceptHandle = AcceptIrp->Tail.Overlay.DriverContext[3];
listenProcess = NULL;
}
//
// Now take care of the accept endpoint
//
AfdAcquireSpinLock (&acceptEndpoint->SpinLock, LockHandle);
IoSetCancelRoutine (AcceptIrp, AfdSanCancelAccept);
if (acceptEndpoint->EndpointCleanedUp || AcceptIrp->Cancel) {
//
// Endpoint was closed or IRP cancelled,
// reset accept irp pointer and return error
// to both application and SAN provider (to refuse
// the connection)
//
AcceptIrp->Tail.Overlay.ListEntry.Flink = NULL;
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, LockHandle);
if (listenProcess!=NULL) {
#if DBG
NTSTATUS status;
#endif
//
// Destroy the handle that we created for
// accepting socket duplication.
//
KeAttachProcess (listenProcess);
#if DBG
status =
#endif
NtClose (acceptHandle);
KeDetachProcess ();
ASSERT (NT_SUCCESS (status));
}
connectIrp->IoStatus.Status = STATUS_CANCELLED;
connectIrp->IoStatus.Information = 0;
IoCompleteRequest (connectIrp, AfdPriorityBoost);
//
// If AcceptIrp's Cancel routine is running, let it finish.
// Once we return, caller will complete the AcceptIrp
//
if (IoSetCancelRoutine (AcceptIrp, NULL)==NULL) {
KIRQL cancelIrql;
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
return STATUS_CANCELLED;
}
//
// Pend the accept IRP till provider
// completes it. This may take quite a while,
// which is not expected by the msafd and application,
// but we have no choice here
//
IoMarkIrpPending (AcceptIrp);
irpSp = IoGetCurrentIrpStackLocation (AcceptIrp);
receiveLength = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength;
connectInfo = connectIrp->AssociatedIrp.SystemBuffer;
//
// Remove super accept IRP from the endpoint
//
acceptEndpoint->Irp = NULL;
//
// Convert endpoint to SAN
//
AfdSanInitEndpoint (IoGetCurrentIrpStackLocation (connectIrp)->FileObject->FsContext,
AcceptFileObject,
connectInfo->SwitchContext);
if (listenProcess!=NULL) {
//
// Note that socket was duplicated implicitly, without application request
//
acceptEndpoint->Common.SanEndp.ImplicitDup = TRUE;
}
UPDATE_ENDPOINT2 (acceptEndpoint, "AfdSanAcceptCore, accepted with bytes: 0x%lX", receiveLength);
InsertTailList (&acceptEndpoint->Common.SanEndp.IrpList,
&AcceptIrp->Tail.Overlay.ListEntry);
if (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength>0) {
ULONG remoteAddressLength =
FIELD_OFFSET (
TRANSPORT_ADDRESS,
Address[0].Address[
connectInfo->RemoteAddress.Address[0].AddressLength]);
#ifndef i386
if (acceptEndpoint->Common.VcConnecting.FixAddressAlignment) {
USHORT addressLength =
connectInfo->RemoteAddress.Address[0].AddressLength
+ sizeof (USHORT);
USHORT UNALIGNED *pAddrLength = (PVOID)
((PUCHAR)MmGetSystemAddressForMdl (AcceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength
- sizeof (USHORT));
RtlMoveMemory (
(PUCHAR)MmGetSystemAddressForMdl (AcceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
&connectInfo->RemoteAddress.Address[0].AddressType,
addressLength);
*pAddrLength = addressLength;
}
else
#endif
{
RtlMoveMemory (
(PUCHAR)MmGetSystemAddressForMdl (AcceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
&connectInfo->RemoteAddress,
remoteAddressLength);
}
if (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0) {
PTA_ADDRESS localAddress = (PTA_ADDRESS)
&(connectInfo->RemoteAddress.Address[0].Address[
connectInfo->RemoteAddress.Address[0].AddressLength]);
TDI_ADDRESS_INFO UNALIGNED *addressInfo = (PVOID)
((PUCHAR)MmGetSystemAddressForMdl(AcceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength);
#ifndef i386
if (acceptEndpoint->Common.VcConnecting.FixAddressAlignment) {
USHORT UNALIGNED * pAddrLength = (PVOID)
((PUCHAR)addressInfo
+irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
-sizeof(USHORT));
RtlMoveMemory (
addressInfo,
&localAddress->AddressType,
localAddress->AddressLength+sizeof (USHORT));
*pAddrLength = localAddress->AddressLength+sizeof (USHORT);
}
else
#endif
{
addressInfo->ActivityCount = 0;
addressInfo->Address.TAAddressCount = 1;
RtlMoveMemory (
&addressInfo->Address.Address,
localAddress,
FIELD_OFFSET (TA_ADDRESS, Address[localAddress->AddressLength]));
}
}
}
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, LockHandle);
//
// Setup the accept info for the provider
//
//
// Do this under protection of exception handler since application
// can change protection attributes of the virtual address range
// or even deallocate it. Make sure to attach to the listening
// process if necessary.
//
if (listenProcess!=NULL) {
KeAttachProcess (listenProcess);
}
else {
ASSERT (IoGetCurrentProcess ()==IoThreadToProcess (connectIrp->Tail.Overlay.Thread));
}
try {
#ifdef _WIN64
if (IoIs32bitProcess (connectIrp)) {
PAFD_SWITCH_ACCEPT_INFO32 acceptInfo32;
acceptInfo32 = MmGetMdlVirtualAddress (connectIrp->MdlAddress);
ASSERT (acceptInfo32!=NULL);
ASSERT (MmGetMdlByteCount (connectIrp->MdlAddress)>=sizeof (*acceptInfo32));
acceptInfo32->AcceptHandle = (VOID * POINTER_32)acceptHandle;
acceptInfo32->ReceiveLength = receiveLength;
connectIrp->IoStatus.Information = sizeof (*acceptInfo32);
}
else
#endif _WIN64
{
PAFD_SWITCH_ACCEPT_INFO acceptInfo;
acceptInfo = MmGetMdlVirtualAddress (connectIrp->MdlAddress);
ASSERT (acceptInfo!=NULL);
ASSERT (MmGetMdlByteCount (connectIrp->MdlAddress)>=sizeof (*acceptInfo));
acceptInfo->AcceptHandle = acceptHandle;
acceptInfo->ReceiveLength = receiveLength;
connectIrp->IoStatus.Information = sizeof (*acceptInfo);
}
connectIrp->IoStatus.Status = (listenProcess==NULL) ? STATUS_SUCCESS : STATUS_MORE_ENTRIES;
}
except (AFD_EXCEPTION_FILTER (connectIrp->IoStatus.Status)) {
//
// If the app is playing with switch's virtual addresses
// we can't help much - it's accept IRP will probably
// just hang since the switch is not going to follow
// the failed connect IRP with accept completion.
//
}
if (listenProcess!=NULL) {
KeDetachProcess ();
}
//
// Complete the provider IRP.
//
IoCompleteRequest (connectIrp, AfdPriorityBoost);
return STATUS_PENDING;
}
VOID
AfdSanReleaseConnection (
PAFD_ENDPOINT ListenEndpoint,
PAFD_CONNECTION Connection,
BOOLEAN CheckBacklog
)
{
LONG failedAdds;
failedAdds = InterlockedDecrement (
&ListenEndpoint->Common.VcListening.FailedConnectionAdds);
if (CheckBacklog || failedAdds>=0) {
if (!IS_DELAYED_ACCEPTANCE_ENDPOINT (ListenEndpoint)) {
InterlockedPushEntrySList (
&ListenEndpoint->Common.VcListening.FreeConnectionListHead,
&Connection->SListEntry
);
return;
}
else {
NTSTATUS status;
status = AfdDelayedAcceptListen (ListenEndpoint, Connection);
if (NT_SUCCESS (status)) {
return;
}
}
}
InterlockedIncrement(
&ListenEndpoint->Common.VcListening.FailedConnectionAdds
);
DEREFERENCE_CONNECTION (Connection);
}
VOID
AfdSanCancelConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancels a connection indication IRP from SAN provider
Arguments:
DeviceObject - not used.
Irp - the IRP to cancel.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpSp;
PAFD_CONNECTION connection;
PAFD_ENDPOINT endpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
UNREFERENCED_PARAMETER (DeviceObject);
irpSp = IoGetCurrentIrpStackLocation (Irp);
connection = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT (connection->Type==AfdBlockTypeConnection);
ASSERT (connection->SanConnection);
endpoint = connection->Endpoint;
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
ASSERT (endpoint->Listening);
ASSERT (KeGetCurrentIrql ()==DISPATCH_LEVEL);
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
//
// If IRP still there, cancel it.
// Otherwise, it is being completed anyway.
//
if (connection->ConnectIrp!=NULL) {
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCancelConnect: endp-%p, irp-%p\n",
endpoint, Irp));
}
ASSERT (connection->ConnectIrp == Irp);
connection->ConnectIrp = NULL;
ASSERT (connection->Endpoint == endpoint);
connection->Endpoint = NULL;
connection->SanConnection = FALSE;
ASSERT (connection->State == AfdConnectionStateUnaccepted ||
connection->State==AfdConnectionStateReturned);
RemoveEntryList (&connection->ListEntry);
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (Irp->CancelIrql);
ASSERT ((endpoint->Type&AfdBlockTypeVcListening)==AfdBlockTypeVcListening);
AfdSanReleaseConnection (endpoint, connection, TRUE);
DEREFERENCE_ENDPOINT (endpoint);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, AfdPriorityBoost);
}
else {
//
// Irp is about to be completed.
//
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (Irp->CancelIrql);
}
}
VOID
AfdSanCancelAccept (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancels an accept IRP from application waiting on accept
completion from SAN
Arguments:
DeviceObject - not used.
Irp - the IRP to cancel.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
UNREFERENCED_PARAMETER (DeviceObject);
irpSp = IoGetCurrentIrpStackLocation (Irp);
acceptFileObject = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
acceptEndpoint = acceptFileObject->FsContext;
ASSERT (acceptEndpoint->Type==AfdBlockTypeSanEndpoint);
ASSERT (KeGetCurrentIrql()==DISPATCH_LEVEL);
AfdAcquireSpinLockAtDpcLevel (&acceptEndpoint->SpinLock, &lockHandle);
//
// If IRP still there, cancel it.
// Otherwise, it is being completed anyway.
//
if (Irp->Tail.Overlay.ListEntry.Flink!=NULL) {
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCancelAccept: endp-%p, irp-%p\n",
acceptEndpoint, Irp));
}
if (!acceptEndpoint->EndpointCleanedUp) {
ASSERT (acceptEndpoint->Common.SanEndp.SanHlpr!=NULL);
DEREFERENCE_ENDPOINT (acceptEndpoint->Common.SanEndp.SanHlpr);
//
// Make sure we cleanup all the fields since they will be
// treated as other part (VC) of the endpoint union.
//
RtlZeroMemory (&acceptEndpoint->Common.SanEndp,
sizeof (acceptEndpoint->Common.SanEndp));
//
// Reinitialize the endpoint structure.
//
acceptEndpoint->Type = AfdBlockTypeEndpoint;
acceptEndpoint->State = AfdEndpointStateOpen;
acceptEndpoint->DisconnectMode = 0;
acceptEndpoint->EndpointStateFlags = 0;
}
AfdReleaseSpinLockFromDpcLevel (&acceptEndpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (Irp->CancelIrql);
AFD_END_STATE_CHANGE (acceptEndpoint);
//
// Dereference accept file object and complete the IRP.
//
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject (acceptFileObject);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest (Irp, AfdPriorityBoost);
}
else {
//
// Irp is about to be completed.
//
AfdReleaseSpinLockFromDpcLevel (&acceptEndpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (Irp->CancelIrql);
}
}
VOID
AfdSanCancelRequest (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancels a redirected IRP
Arguments:
DeviceObject - not used.
Irp - the IRP to cancel.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpSp;
PAFD_ENDPOINT endpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
UNREFERENCED_PARAMETER (DeviceObject);
irpSp = IoGetCurrentIrpStackLocation (Irp);
endpoint = irpSp->FileObject->FsContext;
ASSERT (endpoint->Type==AfdBlockTypeSanEndpoint);
ASSERT (KeGetCurrentIrql ()==DISPATCH_LEVEL);
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
//
// If IRP still there, cancel it.
// Otherwise, it is being completed anyway.
//
if (Irp->Tail.Overlay.ListEntry.Flink!=NULL) {
BOOLEAN needRestartProcessing = FALSE;
RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
if (AFD_SWITCH_REQUEST_TYPE (Irp->AfdSanRequestInfo.AfdSanRequestCtx)
== AFD_SWITCH_REQUEST_TFCTX) {
//
// Request being cancelled is transfer request. We are suspending
// other requests while processing this one, so we need to reset
// the transfer status to allow further requests to proceed and
// if there are some requests pending already, we need to initiate
// them but only AFTER we informed the switch that transfer request
// was cancelled.
//
ASSERT (endpoint->Common.SanEndp.CtxTransferStatus==STATUS_PENDING);
if (!IsListEmpty (&endpoint->Common.SanEndp.IrpList)) {
needRestartProcessing = TRUE;
}
else {
endpoint->Common.SanEndp.CtxTransferStatus = STATUS_SUCCESS;
}
}
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (Irp->CancelIrql);
IF_DEBUG(SAN_SWITCH) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSanCancelRequest: endp-%p, irp-%p, context-%p\n",
endpoint, Irp, Irp->AfdSanRequestInfo.AfdSanRequestCtx));
}
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
if (Irp->AfdSanRequestInfo.AfdSanRequestCtx!=NULL) {
//
// Notify the switch that request was cancelled
//
AfdSanNotifyRequest (endpoint, Irp->AfdSanRequestInfo.AfdSanRequestCtx,
STATUS_CANCELLED,
0);
if (needRestartProcessing) {
AfdSanRestartRequestProcessing (endpoint, STATUS_SUCCESS);
}
}
else {
ASSERT (needRestartProcessing==FALSE);
}
IoCompleteRequest (Irp, AfdPriorityBoost);
}
else {
//
// Irp is about to be completed.
//
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (Irp->CancelIrql);
}
}
NTSTATUS
AfdSanRestartRequestProcessing (
PAFD_ENDPOINT Endpoint,
NTSTATUS Status
)
/*++
Routine Description:
Restarts request processing after completion of the
transfer context request.
Arguments:
Endpoint - endpoint on which to restart request processing
Status - new context transfer status
Return Value:
NTSTATUS previous transfer status
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
NTSTATUS oldStatus;
Again:
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
if (!IS_SAN_ENDPOINT (Endpoint)) {
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
return STATUS_INVALID_HANDLE;
}
oldStatus = Endpoint->Common.SanEndp.CtxTransferStatus;
ASSERT (Status!=STATUS_PENDING && Status!=STATUS_MORE_PROCESSING_REQUIRED);
//
// The enpoint should have just finished transferring context
// from one process to another. The CtxTransferStatus
// should still be pending or failure (e.g. if we jumped with
// goto below while another thread produced a failure).
//
ASSERT (Endpoint->Common.SanEndp.CtxTransferStatus==STATUS_PENDING ||
Endpoint->Common.SanEndp.CtxTransferStatus==STATUS_MORE_PROCESSING_REQUIRED ||
!NT_SUCCESS (Endpoint->Common.SanEndp.CtxTransferStatus) ||
!NT_SUCCESS (Status));
if (!NT_SUCCESS (Status)) {
PLIST_ENTRY irpList = NULL;
PVOID savedContext = NULL;
//
// Cleanup paged pool context that we saved for the switch.
//
if (Endpoint->Common.SanEndp.CtxTransferStatus==STATUS_MORE_PROCESSING_REQUIRED &&
Endpoint->Common.SanEndp.SavedContext!=NULL) {
//
// Save it to free when we release the spinlock - can free
// paged pool at DPC.
//
ASSERT (IS_SYSTEM_ADDRESS (Endpoint->Common.SanEndp.SavedContext));
savedContext = Endpoint->Common.SanEndp.SavedContext;
Endpoint->Common.SanEndp.SavedContext = NULL;
Endpoint->Common.SanEndp.SavedContextLength = 0;
}
//
// Reset the status to failure and get rid of all the requests
// after spinlock is released.
//
Endpoint->Common.SanEndp.CtxTransferStatus = Status;
while (!IsListEmpty (&Endpoint->Common.SanEndp.IrpList)) {
PLIST_ENTRY listEntry;
listEntry = RemoveHeadList (&Endpoint->Common.SanEndp.IrpList);
//
// Mark Flink so that cancel routine knows that request is being completed.
// Use Blink to create a singly-linked list of IRPs to complete.
//
listEntry->Flink = NULL;
listEntry->Blink = irpList;
irpList = listEntry;
}
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
if (savedContext!=NULL) {
InterlockedExchangeAdd (&Endpoint->Common.SanEndp.SanHlpr->Common.SanHlpr.PendingRequests, -2);
AFD_FREE_POOL (savedContext, AFD_SAN_CONTEXT_POOL_TAG);
}
while (irpList!=NULL) {
PIRP irp;
irp = CONTAINING_RECORD (irpList, IRP, Tail.Overlay.ListEntry);
irpList = irp->Tail.Overlay.ListEntry.Blink;
if (irp->AfdSanRequestInfo.AfdSanRequestCtx!=NULL) {
InterlockedExchangeAdd (&Endpoint->Common.SanEndp.SanHlpr->Common.SanHlpr.PendingRequests, -2);
}
//
// Check if request is being cancelled and synchronize
// with the cancel routine if so.
//
if (IoSetCancelRoutine (irp, NULL)==NULL) {
KIRQL cancelIrql;
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
irp->IoStatus.Status = Status;
irp->IoStatus.Information = 0;
IoCompleteRequest (irp, AfdPriorityBoost);
}
}
else if (Endpoint->Common.SanEndp.CtxTransferStatus==STATUS_PENDING ||
Endpoint->Common.SanEndp.CtxTransferStatus==STATUS_MORE_PROCESSING_REQUIRED)
{
PLIST_ENTRY listEntry;
NTSTATUS status;
//
// Scan the list for the request that have not been comminicated
// to the switch.
//
listEntry = Endpoint->Common.SanEndp.IrpList.Flink;
while (listEntry!=&Endpoint->Common.SanEndp.IrpList) {
ULONG_PTR requestInfo;
ULONG requestType;
PVOID requestCtx;
PIRP irp = CONTAINING_RECORD (listEntry,
IRP,
Tail.Overlay.ListEntry);
listEntry = listEntry->Flink;
//
// The transfer was successfull, continue processing requests
//
if (irp->AfdSanRequestInfo.AfdSanRequestCtx==NULL) {
//
// This request has not been communicated to the switch.
//
PIO_STACK_LOCATION irpSp;
//
// Create request context based on request type.
//
irpSp = IoGetCurrentIrpStackLocation (irp);
switch (irpSp->MajorFunction) {
case IRP_MJ_READ:
requestType = AFD_SWITCH_REQUEST_READ;
requestInfo = irpSp->Parameters.Read.Length;
break;
case IRP_MJ_WRITE:
requestType = AFD_SWITCH_REQUEST_WRITE;
requestInfo = irpSp->Parameters.Write.Length;
break;
case IRP_MJ_DEVICE_CONTROL:
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_AFD_SWITCH_ACQUIRE_CTX:
requestType = AFD_SWITCH_REQUEST_TFCTX;
requestInfo = (ULONG_PTR)irp->AfdSanRequestInfo.AfdSanProcessId;
break;
default:
ASSERT (!"Unsupported IOCTL");
__assume (0);
}
break;
default:
ASSERT (!"Unsupported IRP Major Function");
__assume (0);
}
requestCtx = AFD_SWITCH_MAKE_REQUEST_CONTEXT(
Endpoint->Common.SanEndp.RequestId,
requestType);
irp->AfdSanRequestInfo.AfdSanRequestCtx = requestCtx;
Endpoint->Common.SanEndp.RequestId += 1;
UPDATE_ENDPOINT2 (Endpoint,
"AfdSanRestartRequestProcessing, restarting req: 0x%lX",
PtrToUlong (requestCtx));
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
status = AfdSanNotifyRequest (Endpoint, requestCtx, STATUS_SUCCESS, requestInfo);
if (NT_SUCCESS (status)) {
if (requestType==AFD_SWITCH_REQUEST_TFCTX) {
//
// If we successfully sent another context transfer request, we should
// stop processing until this request is completed. The context transfer
// flag should remain set.
//
return oldStatus;
}
}
else {
PIRP irp1;
//
// If notification failed, fail the request. The IRP
// could have been cancelled, so we have to search for
// it in the list.
//
irp1 = AfdSanDequeueRequest (Endpoint, requestCtx);
if (irp1!=NULL) {
ASSERT (irp1==irp);
irp1->IoStatus.Status = status;
IoCompleteRequest (irp1, AfdPriorityBoost);
}
}
//
// Reacquire the lock and continue processing from the beginning
// as the list could have changed while spinlock was released.
//
goto Again;
}
else {
//
// IRP has already been forwarded to the switch, leave it alone
//
}
}
//
// Ran till the end of the list and did not find another transfer request.
// We can reset the flag now.
//
Endpoint->Common.SanEndp.CtxTransferStatus = Status;
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
}
else {
//
// This can only occur if we have already failed before and
// there are thus should be no irps in the list.
//
ASSERT (IsListEmpty (&Endpoint->Common.SanEndp.IrpList));
ASSERT (!NT_SUCCESS (Endpoint->Common.SanEndp.CtxTransferStatus));
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
}
return oldStatus;
}
PIRP
AfdSanDequeueRequest (
PAFD_ENDPOINT SanEndpoint,
PVOID RequestCtx
)
/*++
Routine Description:
Removes the request from the san endpoint list
Arguments:
SanEndpoint - endpoint from which to remove the request
RequestCtx - context that identifies the request
Return Value:
The request or NULL is not found.
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
PLIST_ENTRY listEntry;
ASSERT (RequestCtx!=NULL);
AfdAcquireSpinLock (&SanEndpoint->SpinLock, &lockHandle);
if (!IS_SAN_ENDPOINT (SanEndpoint)) {
AfdReleaseSpinLock (&SanEndpoint->SpinLock, &lockHandle);
return NULL;
}
listEntry = SanEndpoint->Common.SanEndp.IrpList.Flink;
//
// Walk the list till we find the request
//
while (listEntry!=&SanEndpoint->Common.SanEndp.IrpList) {
PIRP irp = CONTAINING_RECORD (listEntry,
IRP,
Tail.Overlay.ListEntry);
listEntry = listEntry->Flink;
if (irp->AfdSanRequestInfo.AfdSanRequestCtx==RequestCtx) {
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
irp->Tail.Overlay.ListEntry.Flink = NULL;
AfdReleaseSpinLock (&SanEndpoint->SpinLock, &lockHandle);
//
// Check if request is being cancelled and synchronize
// with the cancel routine if so.
//
if (IoSetCancelRoutine (irp, NULL)==NULL) {
KIRQL cancelIrql;
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
InterlockedExchangeAdd (&SanEndpoint->Common.SanEndp.SanHlpr->Common.SanHlpr.PendingRequests, -2);
return irp;
}
else if (irp->AfdSanRequestInfo.AfdSanRequestCtx==NULL) {
break;
}
}
AfdReleaseSpinLock (&SanEndpoint->SpinLock, &lockHandle);
return NULL;
}
NTSTATUS
AfdSanNotifyRequest (
PAFD_ENDPOINT SanEndpoint,
PVOID RequestCtx,
NTSTATUS Status,
ULONG_PTR Information
)
{
PVOID context;
PAFD_ENDPOINT sanHlprEndpoint;
NTSTATUS status;
PAGED_CODE ();
context = AfdLockEndpointContext (SanEndpoint);
if (IS_SAN_ENDPOINT (SanEndpoint)) {
//
// Get the san helper endpoint which we use to communicate
// with the switch
//
sanHlprEndpoint = SanEndpoint->Common.SanEndp.SanHlpr;
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
//
// Increment the count of outstanding requests and verify that
// helper endpoint is still active and thus the process can
// accept the requests.
//
if ((InterlockedExchangeAdd (&sanHlprEndpoint->Common.SanHlpr.PendingRequests, 2) & 1)==0) {
ASSERT ((ULONG_PTR)SanEndpoint->Common.SanEndp.SwitchContext<MM_USER_PROBE_ADDRESS); //
// Notify the switch about the request.
//
status = IoSetIoCompletion (
sanHlprEndpoint->Common.SanHlpr.IoCompletionPort,// Port
SanEndpoint->Common.SanEndp.SwitchContext, // Key
RequestCtx, // ApcContext
Status, // Status
Information, // Information
TRUE // ChargeQuota
);
}
else {
status = STATUS_CANCELLED;
}
}
else {
status = STATUS_INVALID_HANDLE;
}
AfdUnlockEndpointContext (SanEndpoint, context);
return status;
}
NTSTATUS
AfdSanPollBegin (
PAFD_ENDPOINT Endpoint,
ULONG EventMask
)
/*++
Routine Description:
Records poll call so that switch knows to notify AFD to complete
select/AsyncSelect/EventSelect requests
Arguments:
Endpoint - endpoint to record
EventMask - events that needs to be notified
Return Value:
NTSTATUS - may fail to access user mode address
Note:
This routine must be called in the context of user mode process
that owns the endpoint
--*/
{
LONG currentEvents, newEvents;
PVOID context;
BOOLEAN attached = FALSE;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE ();
context = AfdLockEndpointContext (Endpoint);
if (IS_SAN_ENDPOINT (Endpoint)) {
if (IoGetCurrentProcess()!=Endpoint->Common.SanEndp.SanHlpr->OwningProcess) {
KeAttachProcess (PsGetProcessPcb(Endpoint->Common.SanEndp.SanHlpr->OwningProcess));
attached = TRUE;
}
try {
//
// Increment appropriate counts to inform the switch that we are interested
// in the event.
//
if (EventMask & AFD_POLL_RECEIVE) {
InterlockedIncrement (&Endpoint->Common.SanEndp.SwitchContext->RcvCount);
//
// Inform switch that select has happened on this endpoint
//
Endpoint->Common.SanEndp.SwitchContext->SelectFlag = TRUE;
}
if (EventMask & AFD_POLL_RECEIVE_EXPEDITED) {
InterlockedIncrement (&Endpoint->Common.SanEndp.SwitchContext->ExpCount);
}
if (EventMask & AFD_POLL_SEND) {
InterlockedIncrement (&Endpoint->Common.SanEndp.SwitchContext->SndCount);
}
//
// Update our event record.
//
do {
currentEvents = *((LONG volatile *)&Endpoint->Common.SanEndp.SelectEventsActive);
newEvents = *((LONG volatile *)&Endpoint->Common.SanEndp.SwitchContext->EventsActive);
}
while (InterlockedCompareExchange (
(PLONG)&Endpoint->Common.SanEndp.SelectEventsActive,
newEvents,
currentEvents)!=currentEvents);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
if (attached) {
KeDetachProcess ();
}
}
AfdUnlockEndpointContext (Endpoint, context);
return status;
}
VOID
AfdSanPollEnd (
PAFD_ENDPOINT Endpoint,
ULONG EventMask
)
/*++
Routine Description:
Records poll call completion, so that switch can avoild expensive calls to notify AFD
to complete select/AsyncSelect/EventSelect requests
Arguments:
Endpoint - endpoint to record
EventMask - events that needs to be dereferenced
Return Value:
NTSTATUS - may fail to access user mode address
Note:
This routine must be called in the context of user mode process
that owns the endpoint
--*/
{
BOOLEAN attached = FALSE;
PVOID context;
PAGED_CODE ();
context = AfdLockEndpointContext (Endpoint);
if (IS_SAN_ENDPOINT (Endpoint)) {
if (IoGetCurrentProcess()!=Endpoint->Common.SanEndp.SanHlpr->OwningProcess) {
KeAttachProcess (PsGetProcessPcb(Endpoint->Common.SanEndp.SanHlpr->OwningProcess));
attached = TRUE;
}
try {
//
// Decrement appropriate counts to inform the switch that we are no longer interested
// in the event.
//
if (EventMask & AFD_POLL_RECEIVE) {
InterlockedDecrement (&Endpoint->Common.SanEndp.SwitchContext->RcvCount);
}
if (EventMask & AFD_POLL_RECEIVE_EXPEDITED) {
InterlockedDecrement (&Endpoint->Common.SanEndp.SwitchContext->ExpCount);
}
if (EventMask & AFD_POLL_SEND) {
InterlockedDecrement (&Endpoint->Common.SanEndp.SwitchContext->SndCount);
}
}
except (AFD_EXCEPTION_FILTER_NO_STATUS()) {
//
// Not much we can do. The switch will have to call us with all the events.
//
}
if (attached) {
KeDetachProcess ();
}
}
AfdUnlockEndpointContext (Endpoint, context);
}
VOID
AfdSanPollUpdate (
PAFD_ENDPOINT Endpoint,
ULONG EventMask
)
/*++
Routine Description:
Updates local kernel information currently outstanding polls on the
endpoint to be later merged into information maitained by the switch
Arguments:
Endpoint - endpoint to record
EventMask - events that needs to be recorded
Return Value:
None
--*/
{
ASSERT (IS_SAN_ENDPOINT (Endpoint));
ASSERT (KeGetCurrentIrql()==DISPATCH_LEVEL);
ASSERT (Endpoint->Common.SanEndp.LocalContext!=NULL);
if (EventMask & AFD_POLL_RECEIVE) {
InterlockedIncrement (&Endpoint->Common.SanEndp.LocalContext->RcvCount);
}
if (EventMask & AFD_POLL_RECEIVE_EXPEDITED) {
InterlockedIncrement (&Endpoint->Common.SanEndp.LocalContext->ExpCount);
}
if (EventMask & AFD_POLL_SEND) {
InterlockedIncrement (&Endpoint->Common.SanEndp.LocalContext->SndCount);
}
}
NTSTATUS
AfdSanPollMerge (
PAFD_ENDPOINT Endpoint,
PAFD_SWITCH_CONTEXT Context
)
/*++
Routine Description:
Merges information about outstanding poll calls into switch counts.
Arguments:
Endpoint - endpoint to record
Context - outstanding select info merge in
Return Value:
NTSTATUS - may fail to access user mode address
Note:
This routine must be called in the context of user mode process
that owns the endpoint
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE ();
ASSERT (IoGetCurrentProcess()==Endpoint->Common.SanEndp.SanHlpr->OwningProcess);
ASSERT (Endpoint->Common.SanEndp.LocalContext == Context);
try {
InterlockedExchangeAdd (&Endpoint->Common.SanEndp.SwitchContext->RcvCount,
Context->RcvCount);
InterlockedExchangeAdd (&Endpoint->Common.SanEndp.SwitchContext->SndCount,
Context->SndCount);
InterlockedExchangeAdd (&Endpoint->Common.SanEndp.SwitchContext->ExpCount,
Context->ExpCount);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
}
return status;
}
VOID
AfdSanInitEndpoint (
PAFD_ENDPOINT SanHlprEndpoint,
PFILE_OBJECT SanFileObject,
PAFD_SWITCH_CONTEXT SwitchContext
)
/*++
Routine Description:
Initializes SAN endpoint structure
Arguments:
SanHlprEndpoint - switch helper endpoint - communication channel
between the switch and AFD for the owner process.
SanFile - file object for the endpoint to be initialized.
Return Value:
None
--*/
{
PAFD_ENDPOINT sanEndpoint = SanFileObject->FsContext;
ASSERT (IS_SAN_HELPER(SanHlprEndpoint));
REFERENCE_ENDPOINT (SanHlprEndpoint);
sanEndpoint->Common.SanEndp.SanHlpr = SanHlprEndpoint;
sanEndpoint->Common.SanEndp.FileObject = SanFileObject;
sanEndpoint->Common.SanEndp.SwitchContext = SwitchContext;
// sanEndpoint->Common.SanEndp.SavedContext = NULL;
sanEndpoint->Common.SanEndp.LocalContext = NULL;
InitializeListHead (&sanEndpoint->Common.SanEndp.IrpList);
sanEndpoint->Common.SanEndp.SelectEventsActive = 0;
sanEndpoint->Common.SanEndp.RequestId = 1;
sanEndpoint->Common.SanEndp.CtxTransferStatus = STATUS_SUCCESS;
sanEndpoint->Common.SanEndp.ImplicitDup = FALSE;
//
// HACKHACK. Force IO subsystem to call us when last handle to the file is closed
// in any given process.
//
SanFileObject->LockOperation = TRUE;
sanEndpoint->Type = AfdBlockTypeSanEndpoint;
}
VOID
AfdSanAbortConnection (
PAFD_CONNECTION Connection
)
{
PIRP connectIrp;
PDRIVER_CANCEL cancelRoutine;
KIRQL cancelIrql;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PAFD_ENDPOINT endpoint = Connection->Endpoint;
ASSERT (Connection->SanConnection==TRUE);
//
// Acquire cancel spinlock and endpoint spinlock in
// this order and recheck the accept IRP
//
IoAcquireCancelSpinLock (&cancelIrql);
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &lockHandle);
connectIrp = Connection->ConnectIrp;
if ((connectIrp!=NULL) &&
((cancelRoutine=IoSetCancelRoutine (connectIrp, NULL))!=NULL)) {
//
// Accept IRP was still there and was not cancelled/completed
// cancel it
//
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
connectIrp->CancelIrql = cancelIrql;
connectIrp->Cancel = TRUE;
(*cancelRoutine) (AfdDeviceObject, connectIrp);
}
else {
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
IoReleaseCancelSpinLock (cancelIrql);
}
}
VOID
AfdSanCleanupEndpoint (
PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Cleans up SAN specific fields in AFD_ENDPOINT
Arguments:
Endpoint - endpoint to cleanup
Return Value:
None
--*/
{
PAFD_ENDPOINT sanHlprEndpoint;
ASSERT (IsListEmpty (&Endpoint->Common.SanEndp.IrpList));
ASSERT (Endpoint->Common.SanEndp.LocalContext == NULL);
sanHlprEndpoint = Endpoint->Common.SanEndp.SanHlpr;
ASSERT (sanHlprEndpoint!=NULL);
ASSERT (IS_SAN_HELPER (sanHlprEndpoint));
DEREFERENCE_ENDPOINT (sanHlprEndpoint);
Endpoint->Common.SanEndp.SanHlpr = NULL;
}
VOID
AfdSanCleanupHelper (
PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Cleans up SAN helper specific fields in AFD_ENDPOINT
Arguments:
Endpoint - endpoint to cleanup
Return Value:
None
--*/
{
ASSERT (Endpoint->Common.SanHlpr.IoCompletionPort!=NULL);
ObDereferenceObject (Endpoint->Common.SanHlpr.IoCompletionPort);
Endpoint->Common.SanHlpr.IoCompletionPort = NULL;
ASSERT (Endpoint->Common.SanHlpr.IoCompletionEvent!=NULL);
ObDereferenceObject (Endpoint->Common.SanHlpr.IoCompletionEvent);
Endpoint->Common.SanHlpr.IoCompletionEvent = NULL;
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
ASSERT (IS_SAN_HELPER (Endpoint));
ASSERT (AfdSanServiceHelper!=Endpoint); // Should have been removed in cleanup.
ASSERT (!IsListEmpty (&Endpoint->Common.SanHlpr.SanListLink));
RemoveEntryList (&Endpoint->Common.SanHlpr.SanListLink);
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
}
VOID
AfdSanHelperCleanup (
PAFD_ENDPOINT SanHlprEndpoint
)
{
if (InterlockedIncrement (&SanHlprEndpoint->Common.SanHlpr.PendingRequests)!=1) {
PLIST_ENTRY listEntry;
//
// Scan the endpoint list and cleanup all requests in the san endpoints
// that refer to this helper endpoint.
//
KeEnterCriticalRegion ();
ExAcquireResourceSharedLite( AfdResource, TRUE );
listEntry = AfdEndpointListHead.Flink;
while (listEntry!=&AfdEndpointListHead) {
PAFD_ENDPOINT sanEndpoint;
sanEndpoint = CONTAINING_RECORD (listEntry, AFD_ENDPOINT, GlobalEndpointListEntry);
if (IS_SAN_ENDPOINT (sanEndpoint) &&
sanEndpoint->Common.SanEndp.SanHlpr==SanHlprEndpoint &&
!IsListEmpty (&sanEndpoint->Common.SanEndp.IrpList)) {
AfdSanRestartRequestProcessing (sanEndpoint, STATUS_CANCELLED);
}
listEntry = listEntry->Flink;
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
}
if (SanHlprEndpoint==AfdSanServiceHelper) {
//
// Last handle to the service helper is being closed in
// the service process (there must be some duplicates around).
// We can no longer count on it, clear our global.
//
KeEnterCriticalRegion ();
ExAcquireResourceExclusiveLite( AfdResource, TRUE );
//
// Re-check under the lock.
//
if (SanHlprEndpoint==AfdSanServiceHelper) {
AfdSanServiceHelper = NULL;
}
ExReleaseResourceLite( AfdResource );
KeLeaveCriticalRegion ();
}
}
BOOLEAN
AfdSanReferenceEndpointObject (
PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Reference file object with which san endpoint is associated
Arguments:
Endpoint - endpoint of interest
Return Value:
TRUE - reference successed
FALSE - endpoint has already been cleaned up and its file object
is about to be closed.
--*/
{
BOOLEAN res = TRUE;
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
if (!Endpoint->EndpointCleanedUp) {
ObReferenceObject (Endpoint->Common.SanEndp.FileObject);
}
else {
res = FALSE;
}
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
return res;
}
NTSTATUS
AfdSanReferenceSwitchSocketByHandle (
IN HANDLE SocketHandle,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode,
IN PAFD_ENDPOINT SanHlprEndpoint,
IN PAFD_SWITCH_CONTEXT SwitchContext OPTIONAL,
OUT PFILE_OBJECT *FileObject
)
/*++
Routine Description:
Finds and validates AFD endpoint based on the handle/context combination passed in
by the switch.
Arguments:
SocketHandle - Socket handle being referenced
DesiredAccess - Required access to the object to perform operation
RequestorMode - Mode of the caller
SanHlprEndpoint - helper endpoint - communication channel between AFD and switch
SwitchContext - context associated by the switch with the socket being referenced
FileObject - file object corresponding to socket handle
Return Value:
STATUS_SUCCESS - operation succeeded
other - failed to find/access endpoint associated with the socket handle/context
--*/
{
NTSTATUS status;
if (IS_SAN_HELPER (SanHlprEndpoint) &&
SanHlprEndpoint->OwningProcess==IoGetCurrentProcess ()) {
status = ObReferenceObjectByHandle (
SocketHandle,
DesiredAccess,
*IoFileObjectType,
RequestorMode,
FileObject,
NULL
);
if (NT_SUCCESS (status) &&
(*FileObject)->DeviceObject==AfdDeviceObject &&
//
// Ether socket belongs to the current process and context matches
// the one supplied by the switch
//
((IS_SAN_ENDPOINT((PAFD_ENDPOINT)(*FileObject)->FsContext) &&
((PAFD_ENDPOINT)((*FileObject)->FsContext))->Common.SanEndp.SanHlpr==SanHlprEndpoint &&
((PAFD_ENDPOINT)((*FileObject)->FsContext))->Common.SanEndp.SwitchContext==SwitchContext)
||
//
// Or this is just a non-SAN socket being converted to one or just
// used for select signalling.
//
(SwitchContext==NULL &&
((PAFD_ENDPOINT)(*FileObject)->FsContext)->Type==AfdBlockTypeEndpoint)) ){
NOTHING;
}
else {
if (NT_SUCCESS (status)) {
//
// Undo object referencing since it doesn't match the one that switch expects
//
ObDereferenceObject (*FileObject);
status = STATUS_INVALID_HANDLE;
}
//
// If switch supplied the context, try to find the socket
// in the current process that has the same one.
//
if (SwitchContext!=NULL) {
status = AfdSanFindSwitchSocketByProcessContext (
status,
SanHlprEndpoint,
SwitchContext,
FileObject);
}
}
}
else
status = STATUS_INVALID_HANDLE;
return status;
}
NTSTATUS
AfdSanFindSwitchSocketByProcessContext (
IN NTSTATUS Status,
IN PAFD_ENDPOINT SanHlprEndpoint,
IN PAFD_SWITCH_CONTEXT SwitchContext,
OUT PFILE_OBJECT *FileObject
)
/*++
Routine Description:
Find SAN endpoint given its process (helper endpoint) and switch context.
Arguments:
Status - status returned by the ob object reference operation
(to be propagated to the caller in case of failure).
SanHlprEndpoint - helper endpoint for the process to look in
SwitchContext - switch context associated with the endpoint
FileObject - returns endpont's file object if found
Return Value:
STATUS_SUCCESS - san endpoint was found
other - failed to find endpoint based on the switch context.
--*/
{
PLIST_ENTRY listEntry;
PAFD_ENDPOINT sanEndpoint = NULL;
HANDLE socketHandle;
PVOID context;
PAGED_CODE ();
//
// Walk the global endpoint list and try to find the entry
// that matches the switch context
//
KeEnterCriticalRegion ();
ExAcquireResourceSharedLite (AfdResource, TRUE);
listEntry = AfdEndpointListHead.Flink;
AFD_W4_INIT context = NULL; // Depends on variable above, but compiler does not see
// the connection.
while (listEntry!=&AfdEndpointListHead) {
sanEndpoint = CONTAINING_RECORD (listEntry, AFD_ENDPOINT, GlobalEndpointListEntry);
context = AfdLockEndpointContext (sanEndpoint);
if (IS_SAN_ENDPOINT (sanEndpoint) &&
sanEndpoint->Common.SanEndp.SanHlpr==SanHlprEndpoint &&
sanEndpoint->Common.SanEndp.SwitchContext==SwitchContext &&
AfdSanReferenceEndpointObject (sanEndpoint)) {
break;
}
AfdUnlockEndpointContext (sanEndpoint, context);
listEntry = listEntry->Flink;
}
ExReleaseResourceLite (AfdResource);
KeLeaveCriticalRegion ();
if (listEntry==&sanEndpoint->GlobalEndpointListEntry) {
//
// Try to find the real handle for the switch to use in the future
//
*FileObject = sanEndpoint->Common.SanEndp.FileObject;
if (ObFindHandleForObject (SanHlprEndpoint->OwningProcess,
sanEndpoint->Common.SanEndp.FileObject,
*IoFileObjectType,
NULL,
&socketHandle)) {
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdSanFindSwitchSocketByProcessContext, handle: 0x%lX",
HandleToUlong (socketHandle));
//
// Notify switch of handle to be used.
// Ignore failure, the switch will still be able to communicate via
// slow lookup path.
//
IoSetIoCompletion (
SanHlprEndpoint->Common.SanHlpr.IoCompletionPort,
SwitchContext,
AFD_SWITCH_MAKE_REQUEST_CONTEXT (0, AFD_SWITCH_REQUEST_CHCTX),
STATUS_SUCCESS,
(ULONG_PTR)socketHandle,
TRUE // Charge quota
);
}
else {
UPDATE_ENDPOINT2 (sanEndpoint,
"AfdSanFindSwitchSocketByProcessContext, object not found from handle: 0x%lX",
HandleToUlong (socketHandle));
}
AfdUnlockEndpointContext (sanEndpoint, context);
Status = STATUS_SUCCESS;
}
return Status;
}
BOOLEAN
AfdSanSetDupingToServiceState (
PAFD_ENDPOINT SanEndpoint
)
/*++
Routine Description:
Reset request pending on SAN endpoint while transfering
context to service process.
Arguments:
SanEndpoint - endpoint on which to reset requests.
Return Value:
TRUE - successfully reset requests and set transfer status
FALSE - the transfer status could not be reset
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
PLIST_ENTRY listEntry;
LONG count = 0;
BOOLEAN res;
AfdAcquireSpinLock (&SanEndpoint->SpinLock, &lockHandle);
listEntry = SanEndpoint->Common.SanEndp.IrpList.Flink;
while (listEntry!=&SanEndpoint->Common.SanEndp.IrpList) {
PIRP irp = CONTAINING_RECORD (listEntry, IRP, Tail.Overlay.ListEntry);
if (irp->AfdSanRequestInfo.AfdSanRequestCtx!=NULL) {
count += 2;
irp->AfdSanRequestInfo.AfdSanRequestCtx = NULL;
}
listEntry = listEntry->Flink;
}
if (NT_SUCCESS (SanEndpoint->Common.SanEndp.CtxTransferStatus)) {
if (SanEndpoint->Common.SanEndp.CtxTransferStatus==STATUS_PENDING)
count += 2;
SanEndpoint->Common.SanEndp.CtxTransferStatus = STATUS_MORE_PROCESSING_REQUIRED;
res = TRUE;
InterlockedExchangeAdd (
&SanEndpoint->Common.SanEndp.SanHlpr->Common.SanHlpr.PendingRequests,
-count);
}
else {
res = FALSE;
}
AfdReleaseSpinLock (&SanEndpoint->SpinLock, &lockHandle);
return res;
}
NTSTATUS
AfdSanSetAskDupeToServiceState (
PAFD_ENDPOINT SanEndpoint
)
/*++
Routine Description:
Initiates the duplication to service process by checking
current duplication state and setting it to STATUS_PENDING
Arguments:
SanEndpoint - endpoint on which to reset requests.
Return Value:
Original value of CtxTransferStatus
--*/
{
NTSTATUS status;
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock (&SanEndpoint->SpinLock, &lockHandle);
status = SanEndpoint->Common.SanEndp.CtxTransferStatus;
if (SanEndpoint->Common.SanEndp.CtxTransferStatus==STATUS_SUCCESS) {
SanEndpoint->Common.SanEndp.CtxTransferStatus = STATUS_PENDING;
}
AfdReleaseSpinLock (&SanEndpoint->SpinLock, &lockHandle);
return status;
}
NTSTATUS
AfdSanDupEndpointIntoServiceProcess (
PFILE_OBJECT SanFileObject,
PVOID SavedContext,
ULONG ContextLength
)
/*++
Routine Description:
Duplicate endpoint into the context of the service process
and save switch context on it
Arguments:
SanFileObject - file object being duplicated
SaveContext - pointer to switch context data
ContextLength - length of the context
Return Value:
STATUS_SUCCESS - successfully duped
other - failed.
--*/
{
NTSTATUS status;
HANDLE handle;
PAFD_ENDPOINT sanEndpoint = SanFileObject->FsContext;
PVOID context;
//
// Take the lock to make sure that service helper won't
// exit on us and take the helper endpoint with it.
//
KeEnterCriticalRegion ();
ExAcquireResourceSharedLite (AfdResource, TRUE);
if (AfdSanServiceHelper!=NULL) {
//
// Attach to the process and create handle for the file object.
//
KeAttachProcess (PsGetProcessPcb(AfdSanServiceHelper->OwningProcess));
status = ObOpenObjectByPointer (
SanFileObject,
OBJ_CASE_INSENSITIVE,
NULL,
MAXIMUM_ALLOWED,
*IoFileObjectType,
KernelMode,
&handle);
KeDetachProcess ();
if (NT_SUCCESS (status)) {
context = AfdLockEndpointContext (sanEndpoint);
//
// Notify the service process that it needs to acquire endpoint context.
//
if (sanEndpoint->Common.SanEndp.SanHlpr!=AfdSanServiceHelper) {
if ((InterlockedExchangeAdd (
&AfdSanServiceHelper->Common.SanHlpr.PendingRequests,
2) & 1)==0) {
status = IoSetIoCompletion (
AfdSanServiceHelper->Common.SanHlpr.IoCompletionPort,
NULL,
AFD_SWITCH_MAKE_REQUEST_CONTEXT (0,AFD_SWITCH_REQUEST_AQCTX),
STATUS_SUCCESS,
(ULONG_PTR)handle,
TRUE // Charge quota
);
if (NT_SUCCESS (status)) {
//
// Check and modify the transfer status state under the
// endpoint spinlock.
//
if (AfdSanSetDupingToServiceState (sanEndpoint)) {
UPDATE_ENDPOINT (sanEndpoint);
DEREFERENCE_ENDPOINT (sanEndpoint->Common.SanEndp.SanHlpr);
REFERENCE_ENDPOINT(AfdSanServiceHelper);
sanEndpoint->Common.SanEndp.SanHlpr = AfdSanServiceHelper;
//sanEndpoint->Common.SanEndp.SwitchContext = NULL;
sanEndpoint->Common.SanEndp.SavedContext = SavedContext;
sanEndpoint->Common.SanEndp.SavedContextLength = ContextLength;
//
// Note that socket was duplicated implicitly without
// application request.
//
sanEndpoint->Common.SanEndp.ImplicitDup = TRUE;
}
else {
//
// Somehow we failed since endpoint in the wrong state
// already. When service process comes back to pick
// it up, we will just fail the call
//
status = STATUS_CANCELLED;
}
AfdUnlockEndpointContext (sanEndpoint, context);
ExReleaseResourceLite (AfdResource);
KeLeaveCriticalRegion ();
return status;
}
}
else {
status = STATUS_CANCELLED;
}
InterlockedExchangeAdd (&AfdSanServiceHelper->Common.SanHlpr.PendingRequests, -2);
}
else {
//
// Endpoint is already in the service process.
//
status = STATUS_INVALID_PARAMETER;
}
AfdUnlockEndpointContext (sanEndpoint, context);
KeAttachProcess (PsGetProcessPcb(sanEndpoint->Common.SanEndp.SanHlpr->OwningProcess));
NtClose (handle);
KeDetachProcess ();
}
}
else {
status = STATUS_UNSUCCESSFUL;
}
ExReleaseResourceLite (AfdResource);
KeLeaveCriticalRegion ();
return status;
}
VOID
AfdSanProcessAddrListForProviderChange (
PAFD_ENDPOINT SpecificEndpoint OPTIONAL
)
/*++
Routine Description:
Fires address list notifications for SAN helper endpoints
to inform switch of Winsock provider list change.
Arguments:
SpecificEndpoint - optinally indentifies specific
helper endpoint to fire notifications for
Return Value:
None.
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
PLIST_ENTRY listEntry;
LIST_ENTRY completedChangeList;
PAFD_ADDRESS_CHANGE change;
PAFD_REQUEST_CONTEXT requestCtx;
PIRP irp;
PIO_STACK_LOCATION irpSp;
PAFD_ENDPOINT endpoint;
LONG plsn;
ASSERT (SpecificEndpoint==NULL || IS_SAN_HELPER (SpecificEndpoint));
//
// Create local list to process notifications after spinlock is released
//
InitializeListHead (&completedChangeList);
//
// Walk the list and move matching notifications to the local list
//
AfdAcquireSpinLock (&AfdAddressChangeLock, &lockHandle);
if (SpecificEndpoint==NULL) {
//
// General notification, increment provider
// list change sequence number.
//
AfdSanProviderListSeqNum += 1;
if (AfdSanProviderListSeqNum==0) {
AfdSanProviderListSeqNum += 1;
}
}
plsn = AfdSanProviderListSeqNum;
listEntry = AfdAddressChangeList.Flink;
while (listEntry!=&AfdAddressChangeList) {
change = CONTAINING_RECORD (listEntry,
AFD_ADDRESS_CHANGE,
ChangeListLink);
listEntry = listEntry->Flink;
if (!change->NonBlocking) {
irp = change->Irp;
irpSp = IoGetCurrentIrpStackLocation (irp);
requestCtx = (PAFD_REQUEST_CONTEXT)&irpSp->Parameters.DeviceIoControl;
endpoint = irpSp->FileObject->FsContext;
ASSERT (change==(PAFD_ADDRESS_CHANGE)irp->Tail.Overlay.DriverContext);
if (IS_SAN_HELPER (endpoint) &&
(SpecificEndpoint==NULL ||
endpoint==SpecificEndpoint)) {
AFD_LOCK_QUEUE_HANDLE endpointLockHandle;
ASSERT (change->AddressType==TDI_ADDRESS_TYPE_IP||
change->AddressType==TDI_ADDRESS_TYPE_IP6);
RemoveEntryList (&change->ChangeListLink);
change->ChangeListLink.Flink = NULL;
//
// If request is already canceled, let cancel routine complete it
//
if (IoSetCancelRoutine (irp, NULL)==NULL) {
continue;
}
AfdAcquireSpinLockAtDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
if (AfdIsRequestInQueue (requestCtx)) {
endpoint->Common.SanHlpr.Plsn = plsn;
UPDATE_ENDPOINT2 (endpoint,
"AfdSanProcessAddrListForProviderChange, new plsn: 0x%lX",
plsn);
//
// Context is still in the list, just remove it so
// no-one can see it anymore and complete
//
RemoveEntryList (&requestCtx->EndpointListLink);
InsertTailList (&completedChangeList,
&change->ChangeListLink);
}
else if (!AfdIsRequestCompleted (requestCtx)) {
//
// During endpoint cleanup, this context was removed from the
// list and cleanup routine is about to be called, don't
// free this IRP until cleanup routine is called
// Also, indicate to the cleanup routine that we are done
// with this IRP and it can free it.
//
AfdMarkRequestCompleted (requestCtx);
}
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &endpointLockHandle);
}
}
}
AfdReleaseSpinLock (&AfdAddressChangeLock, &lockHandle);
//
// Signal interested clients and complete IRPs as necessary
//
while (!IsListEmpty (&completedChangeList)) {
listEntry = RemoveHeadList (&completedChangeList);
change = CONTAINING_RECORD (listEntry,
AFD_ADDRESS_CHANGE,
ChangeListLink);
irp = change->Irp;
irp->IoStatus.Status = STATUS_SUCCESS;
//
// Assigning plsn (can't be 0) distinguishes
// this from regular address list change
// notification.
//
irp->IoStatus.Information = plsn;
IF_DEBUG (ADDRESS_LIST) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdProcessAddressChangeList: Completing change IRP: %p with status: 0 .\n",
irp));
}
IoCompleteRequest (irp, AfdPriorityBoost);
}
}
NTSTATUS
AfdSanGetCompletionObjectTypePointer (
VOID
)
/*++
Routine Description:
Obtains completion port object type pointer for
completion port handle validation purposes.
Note, that this type is not exported from kernel like
most other types that afd uses.
Arguments:
None.
Return Value:
0 - success, other - could not obtain reference.
--*/
{
NTSTATUS status;
UNICODE_STRING obName;
OBJECT_ATTRIBUTES obAttr;
HANDLE obHandle;
PVOID obType;
RtlInitUnicodeString (&obName, L"\\ObjectTypes\\IoCompletion");
InitializeObjectAttributes(
&obAttr,
&obName, // name
OBJ_KERNEL_HANDLE, // attributes
NULL, // root
NULL // security descriptor
);
status = ObOpenObjectByName (
&obAttr, // ObjectAttributes
NULL, // ObjectType
KernelMode, // AccessMode
NULL, // PassedAccessState
0, // DesiredAccess
NULL, // DesiredAccess
&obHandle // Handle
);
if (NT_SUCCESS (status)) {
status = ObReferenceObjectByHandle (
obHandle,
0, // DesiredAccess
NULL, // ObjectType
KernelMode, // AccessMode
&obType, // Object
NULL // HandleInformation
);
ZwClose (obHandle);
if (NT_SUCCESS (status)) {
//
// Make sure we only keep one reference to the object type
//
if (InterlockedCompareExchangePointer (
(PVOID *)&IoCompletionObjectType,
obType,
NULL)!=NULL) {
//
// The reference we have already must be the same
// is we just obtained - there should be only one
// completion object type in the system!
//
ASSERT (obType==(PVOID)IoCompletionObjectType);
//
// Get rid of the extra reference
//
ObDereferenceObject (obType);
}
}
}
return status;
}
/*
SAN SOCKET DUPLICATION AND REDIRECTED REQUEST PROCESSING NOTE
1. Controlling fields in SAN endpoint (AFD_ENDPOINT.Common.SanEndp).
CtxTransferStatus:
STATUS_SUCCESS - default, duplication never been done or succeeded;
STATUS_PENDING - duplication in progress:
a)some process submitted IOCTL_AFD_SWITCH_ACQUIRE_CTX request
and AFD queued AFD_SWITCH_REQUEST_TFCTX to the owner process
of the endpoint;
b)owner process is closing its handle while other processes have
handles opened, so AFD queued AFD_SWITCH_REQUEST_TFCTX
to the owner process;
STATUS_MORE_PROCESSING_REQUIRED - service process duplication
in progress - AFD queued AFD_SWITCH_REQUEST_AQCTX to the service
process after owner process requested duplication upon handle close;
OTHER (failure status) - duplication failed, all redirect and context
transfer request should failed as well.
ImplicitDup:
FALSE - default, duplication never been done or last successful
duplication was explicit.
TRUE - AFD made unsolicited handle duplication:
a)accept was called in the process that doesn't own listening
endpoint and AFD duplicated handle into the listening process,
b)owner process closed the endpoint and AFD queued
AFD_SWITCH_REQUEST_TFCTX to it, the owner process then
responded with IOCTL_AFD_SWITCH_TRANSFER_CTX and AFD
duplicated handle into the service process (and queued
AFD_SWITCH_REQUEST_AQCTX);
IrpList:
List of redirected or IOCTL_AFD_SWITCH_ACQUIRE_CTX requests.
RequestId:
Counter for generation of UNIQUE requests ID's
SavedContext:
Saved user mode context from IOCTL_AFD_SWITCH_TRANSFER_CTX from owner
process that closed its handle while switch negotiates with
the service process to acquire the context.
SavedContextLength:
Length of the context above.
CtxTransferStatus, IrpList, and RequestID are protected with endpoint spinlock
(since they control request queueing and IRP completion/cancellation).
Other fields are protected via the endpoint context lock.
2. Controlling fields in SAN helper (AFD_ENDPOINT.Common.SanHlpr).
PendingRequsts: number of requests submitted to the process via helper
as well as status of the helper (bit 1 set when helper handle was
closed and no new requests can be submitted).
Interlocked operations are used to access this field. Low order bit is
initialized to 0 and set to 1 via InterlockedIncrement when handle
for the helper is closed in the owning process. For each request,
the field is incremented by 2, then the low order bit is checked
to see if handle was closed. If so, request is failed immediately
and counter is decremented back by 2. Upon request completion, counter
is decremented by 2 as well.
3. Processing of redirected requests (AfdSanRedirectRequest).
a)CtxTransferStatus==STATUS_SUCCESS:
set request ID and enqueue it, notify owner process (AfdSanNotifyRequest),
if owner process is gone or notification failed, dequeue and fail
the request (AfdSanDequeueRequest, IoCompleteRequest);
b)CtxTransferStatus==STATUS_PENDING||STATUS_MORE_PROCESSING_REQUIRED
NULL request ID and enqueue it to be processed when outstanding
duplication completes;
c)CtxTransferStatus==OTHER (failure):
fail the request
4. Processing of explicit IOCTL_AFD_SWITCH_ACQUIRE_CTX (generated
by the app when it performed socket call in the process other
than the current owner - AfdSanAcquireContext):
a)CtxTransferStatus==STATUS_SUCCESS:
set request ID, enqueue it, and change CtxTransferStatus
to STATUS_PENDING, notify owner process (AfdSanNotifyRequest),
if owner process is gone or notification failed, dequeue (AfdSanDequeueRequest)
and fail the request (IoCompleteRequest), and reset CtxTransferStatus back
to STATUS_SUCCESS if it is still STATUS_PENDING (AfdSanRestartRequestProcessing -
other transfer requests can still succeed);
b)CtxTransferStatus==STATUS_PENDING||STATUS_MORE_PROCESSING_REQUIRED
NULL request ID and enqueue it to be processed when outstanding
duplication completes;
c)CtxTransferStatus==OTHER:
fail the request
5. Processing of implicit IOCTL_AFD_SWITCH_ACQUIRE_CTX (generated
by the service process on AFD_SWITCH_REQUEST_AQCTX notification):
a)CtxTransferStatus==STATUS_MORE_PROCESSING_REQUIRED:
copy the saved context and data to the request buffer,
complete the acquire request with success,
post all queued requests (AfdSanRestartRequestProcessing) resubmitting those
that have NULL id and reset CtxTransferStatus to STATUS_SUCCESS or
failure if we failed to resumit one of them,
b)CtxTransferStatus==OTHER:
fail the request and mark CtxTransferStatus as STATUS_CANCELLED
6. Processing of solicited IOCTL_AFD_SWITCH_TRANSFER_CTX (generated
by the owner process on AFD_SWITCH_REQUEST_TFCTX notification
for a process that explicitly requested ownership)
a)CtxTransferStatus==STATUS_PENDING:
find corresponding IRP with AFD_SWITCH_REQUEST_TFCTX context,
reset CtxTransferStatus to STATUS_SUCCESS and resume request
processing (resubmit those that have NULL ID);
b)CtxTransferStatus==OTHER or IRP is not found:
fail the request and mark CtxTransferStatus as STATUS_CANCELLED.
7. Processing of unsolicited IOCTL_AFD_SWITCH_TRANSFER_CTX (generated
by the owner process on AFD_SWITCH_REQUEST_TFCTX notification for
a temporary holding in service process):
a)CtxTransferStatus==STATUS_PENDING (or STATUS_SUCCESS-this is not possible
currently since switch does not generate transfers by itself, but in
theory we can do this for WSADuplicateHandle):
save the context, reset CtxTransferStatus to
STATUS_MORE_PROCESSING_REQUIRED, reset all pending request IDs to NULL
for re-processing, queue AFD_SWITCH_REQUEST_AQCTX to service process
to pick up the endpoint;
if anything fails, reset CtxTransferStatus to failure status and fail
all pending requests;
b)CtxTransferStatus==OTHER:
fail the request and mark CtxTransferStatus as STATUS_CANCELLED.
8. Processing of accept from the process that doesn't own listening socket.
Duplicate the accept handle into listening process and mark the endpoint
with ImplicitDup flag. Listening process will then explicitly request
context transfer (e.g. issue IOCTL_AFD_SWITCH_ACQUIRE_CTX).
9. Processing of owner process handle close notification while other
processes still have handles opened (UnlockAll):
a)CtxTransferStatus==STATUS_SUCCESS
Set CtxTransferStatus to STATUS_PENDING and notify owner process
to transfer endpoint to the service process, if notification fails,
reset CtxTransferStatus to STATUS_CANCELLED and fail all pending
requests.
b)CtxTransferStatus==STATUS_PENDING
Some other process (maybe just little while back) asked for the socket
by explicit IOCTL_AFD_SWITCH_ACQUIRE_CTX. Let that duplication proceed.
No need to duplicate to service process.
c)CtxTransferStatus==OTHER
ignore the call - endpoint is either in failed state or another
duplication request is already in progress.
*/