3002 lines
84 KiB
C
3002 lines
84 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
fsd.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the File System Driver for the LAN Manager
|
||
server.
|
||
|
||
Author:
|
||
|
||
Chuck Lenzmeier (chuckl) 22-Sep-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
//
|
||
// This module is laid out as follows:
|
||
// Includes
|
||
// Local #defines
|
||
// Local type definitions
|
||
// Forward declarations of local functions
|
||
// Device driver entry points
|
||
// Server I/O completion routine
|
||
// Server transport event handlers
|
||
// SMB processing support routines
|
||
//
|
||
|
||
#include "precomp.h"
|
||
#include "fsd.tmh"
|
||
#pragma hdrstop
|
||
|
||
#define BugCheckFileId SRV_FILE_FSD
|
||
|
||
extern UNICODE_STRING SrvDeviceName;
|
||
extern UNICODE_STRING SrvRegistryPath;
|
||
|
||
// We allow a connection a couple extra work contexts to cover the MAILSLOT and ECHO operation cases
|
||
#define MAX_MPX_MARGIN 10
|
||
|
||
//
|
||
// Forward declarations
|
||
//
|
||
|
||
NTSTATUS
|
||
DriverEntry (
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
VOID
|
||
UnloadServer (
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( INIT, DriverEntry )
|
||
#pragma alloc_text( PAGE, UnloadServer )
|
||
#pragma alloc_text( PAGE, SrvPnpBindingHandler )
|
||
#pragma alloc_text( PAGE8FIL, SrvFsdOplockCompletionRoutine )
|
||
#pragma alloc_text( PAGE8FIL, SrvFsdRestartSendOplockIItoNone )
|
||
#endif
|
||
#if 0
|
||
NOT PAGEABLE -- SrvFsdIoCompletionRoutine
|
||
NOT PAGEABLE -- SrvFsdSendCompletionRoutine
|
||
NOT PAGEABLE -- SrvFsdTdiConnectHandler
|
||
NOT PAGEABLE -- SrvFsdTdiDisconnectHandler
|
||
NOT PAGEABLE -- SrvFsdTdiReceiveHandler
|
||
NOT PAGEABLE -- SrvFsdGetReceiveWorkItem
|
||
NOT PAGEABLE -- SrvFsdRestartSmbComplete
|
||
NOT PAGEABLE -- SrvFsdRestartSmbAtSendCompletion
|
||
NOT PAGEABLE -- SrvFsdServiceNeedResourceQueue
|
||
NOT PAGEABLE -- SrvAddToNeedResourceQueue
|
||
#endif
|
||
|
||
#if SRVDBG_STATS2
|
||
ULONG IndicationsCopied = 0;
|
||
ULONG IndicationsNotCopied = 0;
|
||
#endif
|
||
|
||
extern BOOLEAN RunSuspectConnectionAlgorithm;
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry (
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the initialization routine for the LAN Manager server file
|
||
system driver. This routine creates the device object for the
|
||
LanmanServer device and performs all other driver initialization.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object created by the system.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status from the initialization operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
CLONG i;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Make sure we haven't messed up the size of a WORK_QUEUE structure.
|
||
// Really needs to be a multiple of CACHE_LINE_SIZE bytes to get
|
||
// proper performance on MP systems.
|
||
//
|
||
// This code gets optimized out when the size is correct.
|
||
//
|
||
if( sizeof( WORK_QUEUE ) & (CACHE_LINE_SIZE-1) ) {
|
||
KdPrint(( "sizeof(WORK_QUEUE) == %d!\n", sizeof( WORK_QUEUE )));
|
||
KdPrint(("Fix the WORK_QUEUE structure to be multiple of CACHE_LINE_SIZE!\n" ));
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#if SRVDBG_BREAK
|
||
KdPrint(( "SRV: At DriverEntry\n" ));
|
||
DbgBreakPoint( );
|
||
#endif
|
||
|
||
#if 0
|
||
SrvDebug.QuadPart = DEBUG_ERRORS | DEBUG_SMB_ERRORS | DEBUG_TDI | DEBUG_PNP;
|
||
#endif
|
||
|
||
IF_DEBUG(FSD1) KdPrint(( "SrvFsdInitialize entered\n" ));
|
||
|
||
#ifdef MEMPRINT
|
||
//
|
||
// Initialize in-memory printing.
|
||
//
|
||
|
||
MemPrintInitialize( );
|
||
#endif
|
||
|
||
//
|
||
// Create the device object. (IoCreateDevice zeroes the memory
|
||
// occupied by the object.)
|
||
//
|
||
// !!! Apply an ACL to the device object.
|
||
//
|
||
|
||
RtlInitUnicodeString(& SrvDeviceName, StrServerDevice);
|
||
|
||
status = IoCreateDevice(
|
||
DriverObject, // DriverObject
|
||
sizeof(DEVICE_EXTENSION), // DeviceExtension
|
||
& SrvDeviceName, // DeviceName
|
||
FILE_DEVICE_NETWORK, // DeviceType
|
||
0, // DeviceCharacteristics
|
||
FALSE, // Exclusive
|
||
&SrvDeviceObject // DeviceObject
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvFsdInitialize: Unable to create device object: %X",
|
||
status,
|
||
NULL
|
||
);
|
||
|
||
SrvLogError(
|
||
DriverObject,
|
||
EVENT_SRV_CANT_CREATE_DEVICE,
|
||
status,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
return status;
|
||
}
|
||
|
||
IF_DEBUG(FSD1) {
|
||
KdPrint(( " Server device object: 0x%p\n", SrvDeviceObject ));
|
||
}
|
||
|
||
//
|
||
// Initialize the driver object for this file system driver.
|
||
//
|
||
|
||
DriverObject->DriverUnload = UnloadServer;
|
||
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
||
DriverObject->MajorFunction[i] = SrvFsdDispatch;
|
||
}
|
||
|
||
//
|
||
// Initialize global data fields.
|
||
//
|
||
|
||
SrvInitializeData( );
|
||
|
||
// Remember registry path
|
||
//
|
||
SrvRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
|
||
SrvRegistryPath.Buffer = ExAllocatePool(PagedPool,
|
||
SrvRegistryPath.MaximumLength);
|
||
if (SrvRegistryPath.Buffer != NULL) {
|
||
RtlCopyUnicodeString(& SrvRegistryPath, RegistryPath);
|
||
}
|
||
else {
|
||
SrvRegistryPath.Length = SrvRegistryPath.MaximumLength = 0;
|
||
}
|
||
|
||
IF_DEBUG(FSD1) KdPrint(( "SrvFsdInitialize complete\n" ));
|
||
|
||
return (status);
|
||
|
||
} // DriverEntry
|
||
|
||
|
||
VOID
|
||
UnloadServer (
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the unload routine for the server driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to server driver object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
ExFreePool(SrvRegistryPath.Buffer);
|
||
|
||
//
|
||
// If we are using a smart card to accelerate direct host IPX clients,
|
||
// let it know we are going away.
|
||
//
|
||
if( SrvIpxSmartCard.DeRegister ) {
|
||
IF_DEBUG( SIPX ) {
|
||
KdPrint(("Calling Smart Card DeRegister\n" ));
|
||
}
|
||
SrvIpxSmartCard.DeRegister();
|
||
}
|
||
|
||
|
||
//
|
||
// Clean up global data structures.
|
||
//
|
||
|
||
SrvTerminateData( );
|
||
|
||
//
|
||
// Delete the server's device object.
|
||
//
|
||
|
||
IoDeleteDevice( SrvDeviceObject );
|
||
|
||
return;
|
||
|
||
} // UnloadServer
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdIoCompletionRoutine (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the I/O completion routine for the server. It is specified
|
||
as the completion routine for asynchronous I/O requests issued by
|
||
the server. It simply calls the restart routine specified for the
|
||
work item when the asynchronous request was started.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
Context - Caller-specified context parameter associated with IRP.
|
||
This is actually a pointer to a Work Context block.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - If STATUS_MORE_PROCESSING_REQUIRED is returned, I/O
|
||
completion processing by IoCompleteRequest terminates its
|
||
operation. Otherwise, IoCompleteRequest continues with I/O
|
||
completion.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
DeviceObject; // prevent compiler warnings
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "SrvFsdIoCompletionRoutine entered for IRP 0x%p\n", Irp ));
|
||
}
|
||
|
||
#if DBG
|
||
if( Irp->Type != (CSHORT) IO_TYPE_IRP ) {
|
||
DbgPrint( "SRV: Irp->Type = %u!\n", Irp->Type );
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Reset the IRP cancelled bit.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
//
|
||
// Call the restart routine associated with the work item.
|
||
//
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "FSD working on work context 0x%p", Context ));
|
||
}
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
((PWORK_CONTEXT)Context)->FsdRestartRoutine( (PWORK_CONTEXT)Context );
|
||
KeLowerIrql( oldIrql );
|
||
|
||
//
|
||
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
|
||
// will stop working on the IRP.
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // SrvFsdIoCompletionRoutine
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdSendCompletionRoutine (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the TDI send completion routine for the server. It simply
|
||
calls the restart routine specified for the work item when the
|
||
send request was started.
|
||
|
||
!!! This routine does the exact same thing as SrvFsdIoCompletionRoutine.
|
||
It offers, however, a convienient network debugging routine since
|
||
it completes only sends.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
Context - Caller-specified context parameter associated with IRP.
|
||
This is actually a pointer to a Work Context block.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - If STATUS_MORE_PROCESSING_REQUIRED is returned, I/O
|
||
completion processing by IoCompleteRequest terminates its
|
||
operation. Otherwise, IoCompleteRequest continues with I/O
|
||
completion.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
PWORK_CONTEXT WorkContext = (PWORK_CONTEXT)(Context);
|
||
DeviceObject; // prevent compiler warnings
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "SrvFsdSendCompletionRoutine entered for IRP 0x%p\n", Irp ));
|
||
}
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Reset the IRP cancelled bit.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
//
|
||
// Call the restart routine associated with the work item.
|
||
//
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "FSD working on work context 0x%p", Context ));
|
||
}
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
((PWORK_CONTEXT)Context)->FsdRestartRoutine( (PWORK_CONTEXT)Context );
|
||
KeLowerIrql( oldIrql );
|
||
|
||
//
|
||
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
|
||
// will stop working on the IRP.
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // SrvFsdSendCompletionRoutine
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdOplockCompletionRoutine (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the I/O completion routine oplock requests.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
Context - A pointer to the oplock context block.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - If STATUS_MORE_PROCESSING_REQUIRED is returned, I/O
|
||
completion processing by IoCompleteRequest terminates its
|
||
operation. Otherwise, IoCompleteRequest continues with I/O
|
||
completion.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRFCB rfcb = Context;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
DeviceObject; // prevent compiler warnings
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "SrvFsdOplockCompletionRoutine entered for IRP 0x%p\n", Irp ));
|
||
}
|
||
|
||
//
|
||
// Queue the oplock context to the FSP work queue, except in the
|
||
// following special case: If a level I oplock request failed, and
|
||
// we want to retry for level II, simply set the oplock retry event
|
||
// and dismiss IRP processing. This is useful because it eliminates
|
||
// a trip to an FSP thread and necessary in order to avoid a
|
||
// deadlock where all of the FSP threads are waiting for their
|
||
// oplock retry events.
|
||
//
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "FSD working on work context 0x%p", Context ));
|
||
}
|
||
|
||
if ( (rfcb->RetryOplockRequest != NULL) &&
|
||
!NT_SUCCESS(Irp->IoStatus.Status) ) {
|
||
|
||
//
|
||
// Set the event that tells the oplock request routine that it
|
||
// is OK to retry the request.
|
||
//
|
||
|
||
IF_DEBUG(OPLOCK) {
|
||
KdPrint(( "SrvFsdOplockCompletionRoutine: oplock retry event "
|
||
"set for RFCB %p\n", rfcb ));
|
||
}
|
||
|
||
KeSetEvent(
|
||
rfcb->RetryOplockRequest,
|
||
EVENT_INCREMENT,
|
||
FALSE );
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
}
|
||
|
||
//
|
||
// Insert the RFCB at the tail of the nonblocking work queue.
|
||
//
|
||
|
||
rfcb->FspRestartRoutine = SrvOplockBreakNotification;
|
||
|
||
SrvInsertWorkQueueTail(
|
||
rfcb->Connection->PreferredWorkQueue,
|
||
(PQUEUEABLE_BLOCK_HEADER)rfcb
|
||
);
|
||
|
||
//
|
||
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
|
||
// will stop working on the IRP.
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // SrvFsdOplockCompletionRoutine
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdTdiConnectHandler(
|
||
IN PVOID TdiEventContext,
|
||
IN int RemoteAddressLength,
|
||
IN PVOID RemoteAddress,
|
||
IN int UserDataLength,
|
||
IN PVOID UserData,
|
||
IN int OptionsLength,
|
||
IN PVOID Options,
|
||
OUT CONNECTION_CONTEXT *ConnectionContext,
|
||
OUT PIRP *AcceptIrp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the transport connect event handler for the server. It is
|
||
specified as the connect handler for all endpoints opened by the
|
||
server. It attempts to dequeue a free connection from a list
|
||
anchored in the endpoint. If successful, it returns the connection
|
||
to the transport. Otherwise, the connection is rejected.
|
||
|
||
Arguments:
|
||
|
||
TdiEventContext -
|
||
|
||
RemoteAddressLength -
|
||
|
||
RemoteAddress -
|
||
|
||
UserDataLength -
|
||
|
||
UserData -
|
||
|
||
OptionsLength -
|
||
|
||
Options -
|
||
|
||
ConnectionContext -
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - !!! (apparently ignored by transport driver)
|
||
|
||
--*/
|
||
|
||
{
|
||
PENDPOINT endpoint;
|
||
PLIST_ENTRY listEntry;
|
||
PCONNECTION connection;
|
||
PWORK_CONTEXT workContext;
|
||
PTA_NETBIOS_ADDRESS address;
|
||
KIRQL oldIrql;
|
||
PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
|
||
|
||
UserDataLength, UserData; // avoid compiler warnings
|
||
OptionsLength, Options;
|
||
|
||
endpoint = (PENDPOINT)TdiEventContext;
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "SrvFsdTdiConnectHandler entered for endpoint 0x%p\n",
|
||
endpoint ));
|
||
}
|
||
|
||
if( SrvCompletedPNPRegistration == FALSE ) {
|
||
//
|
||
// Do not become active on any single transport until all of the
|
||
// transports have been registered
|
||
//
|
||
return STATUS_REQUEST_NOT_ACCEPTED;
|
||
}
|
||
|
||
//
|
||
// Take a receive work item off the free list.
|
||
//
|
||
|
||
ALLOCATE_WORK_CONTEXT( queue, &workContext );
|
||
|
||
if ( workContext == NULL ) {
|
||
|
||
//
|
||
// We're out of WorkContext structures, and we aren't able to allocate
|
||
// any more just now. Let's at least cause a worker thread to allocate some more
|
||
// by incrementing the NeedWorkItem counter. This will cause the next
|
||
// freed WorkContext structure to get dispatched to SrvServiceWorkItemShortage.
|
||
// While SrvServiceWorkItemShortage probably won't find any work to do, it will
|
||
// allocate more WorkContext structures if it can. Clients generally retry
|
||
// on connection attempts -- perhaps we'll have a free WorkItem structure next time.
|
||
//
|
||
|
||
InterlockedIncrement( &queue->NeedWorkItem );
|
||
|
||
// Set it up to refill the connection cache
|
||
// We need to do this because previous attempts could have failed due to
|
||
// out-of-memory, leaving us in a state where we don't try to refill anymore
|
||
if( GET_BLOCK_STATE(endpoint) == BlockStateActive )
|
||
{
|
||
SrvResourceFreeConnection = TRUE;
|
||
SrvFsdQueueExWorkItem(
|
||
&SrvResourceThreadWorkItem,
|
||
&SrvResourceThreadRunning,
|
||
CriticalWorkQueue
|
||
);
|
||
}
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvFsdTdiConnectHandler: no work item available",
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
|
||
ACQUIRE_DPC_GLOBAL_SPIN_LOCK( Fsd );
|
||
|
||
//
|
||
// Take a connection off the endpoint's free connection list.
|
||
//
|
||
// *** Note that all of the modifications done to the connection
|
||
// block are done with the spin lock held. This ensures that
|
||
// closing of the endpoint's connections will work properly
|
||
// if it happens simultaneously. Note that we assume that the
|
||
// endpoint is active here. When the TdiAccept completes, we
|
||
// check the endpoint state.
|
||
//
|
||
|
||
listEntry = RemoveHeadList( &endpoint->FreeConnectionList );
|
||
|
||
if ( listEntry == &endpoint->FreeConnectionList ) {
|
||
|
||
//
|
||
// Unable to get a free connection.
|
||
//
|
||
// Dereference the work item manually. We cannot call
|
||
// SrvDereferenceWorkItem from here.
|
||
//
|
||
|
||
RELEASE_DPC_GLOBAL_SPIN_LOCK( Fsd );
|
||
KeLowerIrql( oldIrql );
|
||
|
||
ASSERT( workContext->BlockHeader.ReferenceCount == 1 );
|
||
workContext->BlockHeader.ReferenceCount = 0;
|
||
|
||
RETURN_FREE_WORKITEM( workContext );
|
||
|
||
|
||
IF_DEBUG(TDI) {
|
||
KdPrint(( "SrvFsdTdiConnectHandler: no connection available\n" ));
|
||
}
|
||
|
||
SrvOutOfFreeConnectionCount++;
|
||
|
||
// Set it up to refill the connection cache
|
||
// We need to do this because previous attempts could have failed due to
|
||
// out-of-memory, leaving us in a state where we don't try to refill anymore
|
||
if( GET_BLOCK_STATE(endpoint) == BlockStateActive )
|
||
{
|
||
SrvResourceFreeConnection = TRUE;
|
||
SrvFsdQueueExWorkItem(
|
||
&SrvResourceThreadWorkItem,
|
||
&SrvResourceThreadRunning,
|
||
CriticalWorkQueue
|
||
);
|
||
}
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
endpoint->FreeConnectionCount--;
|
||
|
||
//
|
||
// Wake up the resource thread to create a new free connection for
|
||
// the endpoint.
|
||
//
|
||
|
||
if ( (endpoint->FreeConnectionCount < SrvFreeConnectionMinimum) &&
|
||
(GET_BLOCK_STATE(endpoint) == BlockStateActive) ) {
|
||
SrvResourceFreeConnection = TRUE;
|
||
SrvFsdQueueExWorkItem(
|
||
&SrvResourceThreadWorkItem,
|
||
&SrvResourceThreadRunning,
|
||
CriticalWorkQueue
|
||
);
|
||
}
|
||
|
||
RELEASE_DPC_GLOBAL_SPIN_LOCK( Fsd );
|
||
|
||
//
|
||
// Reference the connection twice -- once to account for its being
|
||
// "open", and once to account for the Accept request we're about
|
||
// to issue.
|
||
//
|
||
|
||
connection = CONTAINING_RECORD(
|
||
listEntry,
|
||
CONNECTION,
|
||
EndpointFreeListEntry
|
||
);
|
||
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
#if SRVDBG29
|
||
if ( GET_BLOCK_STATE(connection) == BlockStateActive ) {
|
||
KdPrint(( "SRV: Connection %x is ACTIVE on free connection list!\n", connection ));
|
||
DbgBreakPoint( );
|
||
}
|
||
if ( connection->BlockHeader.ReferenceCount != 0 ) {
|
||
KdPrint(( "SRV: Connection %x has nonzero refcnt on free connection list!\n", connection ));
|
||
DbgBreakPoint( );
|
||
}
|
||
UpdateConnectionHistory( "CONN", endpoint, connection );
|
||
#endif
|
||
|
||
SrvReferenceConnectionLocked( connection );
|
||
SrvReferenceConnectionLocked( connection );
|
||
|
||
//
|
||
// Indicate that we are a VC-oriented connection
|
||
//
|
||
connection->DirectHostIpx = FALSE;
|
||
|
||
//
|
||
// Set the processor affinity
|
||
//
|
||
connection->PreferredWorkQueue = queue;
|
||
connection->CurrentWorkQueue = queue;
|
||
|
||
InterlockedIncrement( &queue->CurrentClients );
|
||
|
||
#if MULTIPROCESSOR
|
||
//
|
||
// Get this client onto the best processor
|
||
//
|
||
SrvBalanceLoad( connection );
|
||
#endif
|
||
|
||
//
|
||
// Initialize the SMB security signature handling
|
||
//
|
||
connection->SmbSecuritySignatureActive = FALSE;
|
||
|
||
//
|
||
// Put the work item on the in-progress list.
|
||
//
|
||
|
||
SrvInsertTailList(
|
||
&connection->InProgressWorkItemList,
|
||
&workContext->InProgressListEntry
|
||
);
|
||
connection->InProgressWorkContextCount++;
|
||
|
||
//
|
||
// Set the last used timestamp for this connection
|
||
//
|
||
GET_SERVER_TIME( connection->CurrentWorkQueue, &connection->LastRequestTime );
|
||
|
||
//
|
||
// Mark the connection active.
|
||
//
|
||
|
||
SET_BLOCK_STATE( connection, BlockStateActive );
|
||
|
||
//
|
||
// Now we can release the spin lock.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
//
|
||
// Save the client's address/name in the connection block.
|
||
//
|
||
// *** This code only handles NetBIOS names!
|
||
//
|
||
|
||
address = (PTA_NETBIOS_ADDRESS)RemoteAddress;
|
||
ASSERT( address->TAAddressCount == 1 );
|
||
ASSERT( address->Address[0].AddressType == TDI_ADDRESS_TYPE_NETBIOS );
|
||
ASSERT( address->Address[0].AddressLength == sizeof(TDI_ADDRESS_NETBIOS) );
|
||
ASSERT( address->Address[0].Address[0].NetbiosNameType ==
|
||
TDI_ADDRESS_NETBIOS_TYPE_UNIQUE );
|
||
|
||
//
|
||
// Copy the oem name at this time. We convert it to unicode when
|
||
// we get to the fsp.
|
||
//
|
||
|
||
{
|
||
ULONG len;
|
||
PCHAR oemClientName = address->Address[0].Address[0].NetbiosName;
|
||
ULONG oemClientNameLength =
|
||
(MIN( RemoteAddressLength, COMPUTER_NAME_LENGTH ));
|
||
|
||
PCHAR clientMachineName = connection->OemClientMachineName;
|
||
|
||
RtlCopyMemory(
|
||
clientMachineName,
|
||
oemClientName,
|
||
oemClientNameLength
|
||
);
|
||
|
||
clientMachineName[oemClientNameLength] = '\0';
|
||
|
||
//
|
||
// Determine the number of characters that aren't blanks. This is
|
||
// used by the session APIs to simplify their processing.
|
||
//
|
||
|
||
for ( len = oemClientNameLength;
|
||
len > 0 &&
|
||
(clientMachineName[len-1] == ' ' ||
|
||
clientMachineName[len-1] == '\0');
|
||
len-- ) ;
|
||
|
||
connection->OemClientMachineNameString.Length = (USHORT)len;
|
||
|
||
}
|
||
|
||
IF_DEBUG(TDI) {
|
||
KdPrint(( "SrvFsdTdiConnectHandler accepting connection from %z on connection %p\n",
|
||
(PCSTRING)&connection->OemClientMachineNameString, connection ));
|
||
}
|
||
|
||
//
|
||
// Convert the prebuilt TdiReceive request into a TdiAccept request.
|
||
//
|
||
|
||
workContext->Connection = connection;
|
||
workContext->Endpoint = endpoint;
|
||
|
||
(VOID)SrvBuildIoControlRequest(
|
||
workContext->Irp, // input IRP address
|
||
connection->FileObject, // target file object address
|
||
workContext, // context
|
||
IRP_MJ_INTERNAL_DEVICE_CONTROL, // major function
|
||
TDI_ACCEPT, // minor function
|
||
NULL, // input buffer address
|
||
0, // input buffer length
|
||
NULL, // output buffer address
|
||
0, // output buffer length
|
||
NULL, // MDL address
|
||
NULL // completion routine
|
||
);
|
||
|
||
//
|
||
// Make the next stack location current. Normally IoCallDriver would
|
||
// do this, but since we're bypassing that, we do it directly.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation( workContext->Irp );
|
||
|
||
//
|
||
// Set up the restart routine. This routine will verify that the
|
||
// endpoint is active when the TdiAccept completes; if it isn't, the
|
||
// connection will be closed at that time.
|
||
//
|
||
|
||
workContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
||
workContext->FspRestartRoutine = SrvRestartAccept;
|
||
|
||
//
|
||
// Return the connection context (the connection address) to the
|
||
// transport. Return a pointer to the Accept IRP. Indicate that
|
||
// the Connect event has been handled.
|
||
//
|
||
|
||
*ConnectionContext = connection;
|
||
*AcceptIrp = workContext->Irp;
|
||
|
||
KeLowerIrql( oldIrql );
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // SrvFsdTdiConnectHandler
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdTdiDisconnectHandler(
|
||
IN PVOID TdiEventContext,
|
||
IN CONNECTION_CONTEXT ConnectionContext,
|
||
IN int DisconnectDataLength,
|
||
IN PVOID DisconnectData,
|
||
IN int DisconnectInformationLength,
|
||
IN PVOID DisconnectInformation,
|
||
IN ULONG DisconnectFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the transport disconnect event handler for the server. It
|
||
is specified as the disconnect handler for all endpoints opened by
|
||
the server. It attempts to dequeue a preformatted receive item from
|
||
a list anchored in the device object. If successful, it turns this
|
||
receive item into a disconnect item and queues it to the FSP work
|
||
queue. Otherwise, the resource thread is started to format
|
||
additional work items and service pended (dis)connections.
|
||
|
||
Arguments:
|
||
|
||
TransportEndpoint - Pointer to file object for receiving endpoint
|
||
|
||
ConnectionContext - Value associated with endpoint by owner. For
|
||
the server, this points to a Connection block maintained in
|
||
nonpaged pool.
|
||
|
||
DisconnectIndicators - Set of flags indicating the status of the
|
||
disconnect
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - !!! (apparently ignored by transport driver)
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION connection;
|
||
KIRQL oldIrql;
|
||
|
||
TdiEventContext, DisconnectDataLength, DisconnectData;
|
||
DisconnectInformationLength, DisconnectInformation, DisconnectFlags;
|
||
|
||
if( DisconnectFlags & TDI_DISCONNECT_ABORT ) {
|
||
SrvAbortiveDisconnects++;
|
||
}
|
||
|
||
connection = (PCONNECTION)ConnectionContext;
|
||
|
||
#if SRVDBG29
|
||
UpdateConnectionHistory( "DISC", connection->Endpoint, connection );
|
||
#endif
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "SrvFsdTdiDisconnectHandler entered for endpoint 0x%p, connection 0x%p\n", TdiEventContext, connection ));
|
||
}
|
||
|
||
IF_DEBUG(TDI) {
|
||
KdPrint(( "SrvFsdTdiDisconnectHandler received disconnect from %z on connection %p\n",
|
||
(PCSTRING)&connection->OemClientMachineNameString, connection ));
|
||
}
|
||
|
||
//
|
||
// Mark the connection and wake up the resource thread so that it
|
||
// can service the pending (dis)connections.
|
||
//
|
||
|
||
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
|
||
ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
//
|
||
// If the connection is already closing, don't bother queueing it to
|
||
// the disconnect queue.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
if ( connection->DisconnectPending ) {
|
||
|
||
//
|
||
// Error! Error! Error! This connection is already on
|
||
// a queue for processing. Ignore the disconnect request.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_UNEXPECTED,
|
||
"SrvFsdTdiDisconnectHandler: Received unexpected disconnect"
|
||
"indication",
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
SrvLogSimpleEvent( EVENT_SRV_UNEXPECTED_DISC, STATUS_SUCCESS );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
connection->DisconnectPending = TRUE;
|
||
|
||
if ( connection->OnNeedResourceQueue ) {
|
||
|
||
//
|
||
// This connection is waiting for a resource. Take it
|
||
// off the need resource queue before putting it on the
|
||
// disconnect queue.
|
||
//
|
||
// *** Note that the connection has already been referenced to
|
||
// account for its being on the need-resource queue, so we
|
||
// don't reference it again here.
|
||
//
|
||
|
||
SrvRemoveEntryList(
|
||
&SrvNeedResourceQueue,
|
||
&connection->ListEntry
|
||
);
|
||
connection->OnNeedResourceQueue = FALSE;
|
||
|
||
DEBUG connection->ReceivePending = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The connection isn't already on the need-resource queue, so
|
||
// we need to reference it before we put it on the disconnect
|
||
// queue. This is necessary in order to make the code in
|
||
// scavengr.c that removes things from the queue work right.
|
||
//
|
||
|
||
SrvReferenceConnectionLocked( connection );
|
||
|
||
}
|
||
|
||
connection->DisconnectReason = DisconnectTransportIssuedDisconnect;
|
||
SrvInsertTailList(
|
||
&SrvDisconnectQueue,
|
||
&connection->ListEntry
|
||
);
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
SrvResourceDisconnectPending = TRUE;
|
||
SrvFsdQueueExWorkItem(
|
||
&SrvResourceThreadWorkItem,
|
||
&SrvResourceThreadRunning,
|
||
CriticalWorkQueue
|
||
);
|
||
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // SrvFsdTdiDisconnectHandler
|
||
|
||
BOOLEAN
|
||
SrvFsdAcceptReceive(
|
||
PCONNECTION Connection,
|
||
PBYTE Data,
|
||
ULONG BytesIndicated,
|
||
ULONG BytesAvailible
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allows us to trivially reject a receive if we don't think its valid.
|
||
This can be expanded later to include maintaining a list of bad IP addresses and other
|
||
such DoS schemes to help protect us more.
|
||
|
||
Arguments:
|
||
|
||
Connection - the connection this was received on
|
||
|
||
Data - Pointer to the availible data
|
||
|
||
BytesIndicated - the total size of the receive
|
||
|
||
Bytes Availible - How much is currently pointed at by the Data pointer.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Accept receive, FALSE = reject receive
|
||
|
||
--*/
|
||
|
||
{
|
||
PSMB_HEADER pHeader = (PSMB_HEADER)Data;
|
||
|
||
//
|
||
// Trivially reject certain packets
|
||
//
|
||
if( BytesIndicated < sizeof(SMB_HEADER)+sizeof(BYTE) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
if( BytesAvailible > sizeof(SMB_HEADER) )
|
||
{
|
||
if( SmbGetUlong(Data) != SMB_HEADER_PROTOCOL )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
if( Connection->SmbDialect == SmbDialectIllegal &&
|
||
pHeader->Command != SMB_COM_NEGOTIATE )
|
||
{
|
||
return FALSE;
|
||
}
|
||
else if( Connection->SmbDialect != SmbDialectIllegal &&
|
||
pHeader->Command == SMB_COM_NEGOTIATE )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
if( SrvSmbIndexTable[pHeader->Command] == ISrvSmbIllegalCommand )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
} // SrvFsdAcceptReceive
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdTdiReceiveHandler (
|
||
IN PVOID TdiEventContext,
|
||
IN CONNECTION_CONTEXT ConnectionContext,
|
||
IN ULONG ReceiveFlags,
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
OUT ULONG *BytesTaken,
|
||
IN PVOID Tsdu,
|
||
OUT PIRP *IoRequestPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the transport receive event handler for the server. It is
|
||
specified as the receive handler for all endpoints opened by the
|
||
server. It attempts to dequeue a preformatted work item from a list
|
||
anchored in the device object. If this is successful, it returns
|
||
the IRP associated with the work item to the transport provider to
|
||
be used to receive the data. Otherwise, the resource thread is
|
||
awakened to format additional receive work items and service pended
|
||
connections.
|
||
|
||
Arguments:
|
||
|
||
TransportEndpoint - Pointer to file object for receiving endpoint
|
||
|
||
ConnectionContext - Value associated with endpoint by owner. For
|
||
the server, this points to a Connection block maintained in
|
||
nonpaged pool.
|
||
|
||
ReceiveIndicators - Set of flags indicating the status of the
|
||
received message
|
||
|
||
Tsdu - Pointer to received data.
|
||
|
||
Irp - Returns a pointer to I/O request packet, if the returned
|
||
status is STATUS_MORE_PROCESSING_REQUIRED. This IRP is
|
||
made the 'current' Receive for the connection.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - If STATUS_SUCCESS, the receive handler completely
|
||
processed the request. If STATUS_MORE_PROCESSING_REQUIRED,
|
||
the Irp parameter points to a formatted Receive request to
|
||
be used to receive the data. If STATUS_DATA_NOT_ACCEPTED,
|
||
no IRP is returned, but the transport provider should check
|
||
for previously queued Receive requests.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PCONNECTION connection;
|
||
PWORK_CONTEXT workContext;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PWORK_QUEUE queue;
|
||
ULONG receiveLength;
|
||
LONG OplocksInProgress;
|
||
PMDL mdl;
|
||
|
||
KIRQL oldIrql;
|
||
|
||
TdiEventContext; // prevent compiler warnings
|
||
|
||
connection = (PCONNECTION)ConnectionContext;
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "SrvFsdTdiReceiveHandler entered for endpoint 0x%p, "
|
||
"connection 0x%p\n", TdiEventContext, connection ));
|
||
}
|
||
|
||
//
|
||
// If the connection is closing, don't bother servicing this
|
||
// indication.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE(connection) == BlockStateActive ) {
|
||
|
||
// See if we can trivially reject this receive
|
||
if( !SrvFsdAcceptReceive( connection, Tsdu, BytesIndicated, BytesAvailable ) )
|
||
{
|
||
// Mark them as suspect. If a DoS is triggered, they will be nuked first.
|
||
connection->IsConnectionSuspect = TRUE;
|
||
return STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
//
|
||
// Set the last used timestamp for this connection
|
||
//
|
||
GET_SERVER_TIME( connection->CurrentWorkQueue, &connection->LastRequestTime );
|
||
|
||
if ( !(ReceiveFlags & TDI_RECEIVE_AT_DISPATCH_LEVEL) ) {
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
}
|
||
|
||
#if MULTIPROCESSOR
|
||
//
|
||
// See if it's time to home this connection to another
|
||
// processor
|
||
//
|
||
if( --(connection->BalanceCount) == 0 ) {
|
||
SrvBalanceLoad( connection );
|
||
}
|
||
#endif
|
||
|
||
queue = connection->CurrentWorkQueue;
|
||
|
||
//
|
||
// We're going to either get a free work item and point it to
|
||
// the connection, or put the connection on the need-resource
|
||
// queue, so we'll need to reference the connection block.
|
||
//
|
||
// *** Note that we are able to access the connection block
|
||
// because it's in nonpaged pool. Referencing the
|
||
// connection block here accounts for the I/O request we're
|
||
// 'starting', and prevents the block from being deleted
|
||
// until the FSP processes the completed Receive. To make
|
||
// all this work right, the transport provider must
|
||
// guarantee that it won't deliver any events after it
|
||
// delivers a Disconnect event or completes a client-issued
|
||
// Disconnect request.
|
||
//
|
||
// *** Note that we don't reference the endpoint file object
|
||
// directly. The connection block, which we do reference,
|
||
// references an endpoint block, which in turn references
|
||
// the file object.
|
||
//
|
||
|
||
//
|
||
// Try to dequeue a work item from the free list.
|
||
//
|
||
|
||
// Make sure we're not trying to do too much
|
||
|
||
// Note the 2x in this calculation. The reason for this is that if you have a fast client and a busy server,
|
||
// the client can actually turn around and re-use the MID for an operation while the completion call for this
|
||
// operation is still stuck in our queue. This means that it is possible for the client to legally have up to
|
||
// 2x MpxCt items in our queue. This is an upper-bound to ensure compatibility. Note that this is only the first
|
||
// line of defense against DoS, so it is not too bad to allow this many ops through. If they actually get multiple
|
||
// connections going and run us out of resources, we will kick in and start aggressively disconnecting suspicious
|
||
// connections.
|
||
if( (connection->InProgressWorkContextCount > 2*(SrvMaxMpxCount + MAX_MPX_MARGIN)) && !SrvDisableDoSChecking )
|
||
{
|
||
PSMB_HEADER SmbHeader = (PSMB_HEADER)Tsdu;
|
||
|
||
// We normally don't overrun the above bounds. If we do, lets validate that we are truly exceeding our
|
||
// bounds including oplock breaks. Still no good way to include Mailslot operations and echos, so we have a fudge factor.
|
||
// Note that the need to do 2x covers this too.
|
||
OplocksInProgress = InterlockedCompareExchange( &connection->OplockBreaksInProgress, 0, 0 );
|
||
|
||
if( !(connection->InProgressWorkContextCount > 2*(SrvMaxMpxCount + OplocksInProgress + MAX_MPX_MARGIN)) )
|
||
{
|
||
goto abort_dos;
|
||
}
|
||
|
||
// We're going to enforce the max number of work-items a single client can claim
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvFsdTdiReceiveHandler: client overruning their WorkItem limit",
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
connection->IsConnectionSuspect = TRUE;
|
||
RunSuspectConnectionAlgorithm = TRUE;
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
else
|
||
{
|
||
abort_dos:
|
||
ALLOCATE_WORK_CONTEXT( queue, &workContext );
|
||
|
||
if ( workContext != NULL ) {
|
||
|
||
//
|
||
// We found a work item to handle this receive. Reference
|
||
// the connection. Put the work item on the in-progress
|
||
// list. Save the connection and endpoint block addresses
|
||
// in the work context block.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
SrvReferenceConnectionLocked( connection );
|
||
|
||
SrvInsertTailList(
|
||
&connection->InProgressWorkItemList,
|
||
&workContext->InProgressListEntry
|
||
);
|
||
|
||
connection->InProgressWorkContextCount++;
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
//
|
||
// Keep track of the amount of data that was indicated
|
||
//
|
||
workContext->BytesAvailable = BytesAvailable;
|
||
|
||
irp = workContext->Irp;
|
||
|
||
workContext->Connection = connection;
|
||
workContext->Endpoint = connection->Endpoint;
|
||
|
||
if( connection->SmbSecuritySignatureActive &&
|
||
BytesIndicated >= FIELD_OFFSET( SMB_HEADER, Command ) ) {
|
||
|
||
//
|
||
// Save this security signature index
|
||
//
|
||
workContext->SmbSecuritySignatureIndex =
|
||
connection->SmbSecuritySignatureIndex++;
|
||
|
||
//
|
||
// And if we don't have a CANCEL smb, save the response
|
||
// security signature index. We skip this for CANCEL, because
|
||
// CANCEL has no response SMB
|
||
//
|
||
if( ((PSMB_HEADER)Tsdu)->Command != SMB_COM_NT_CANCEL ) {
|
||
workContext->ResponseSmbSecuritySignatureIndex =
|
||
|
||
connection->SmbSecuritySignatureIndex++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the entire SMB has been received, and it is completely
|
||
// within the indicated data, copy it directly into the
|
||
// buffer, avoiding the overhead of passing an IRP down to
|
||
// the transport.
|
||
//
|
||
|
||
if ( ((ReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE) != 0) &&
|
||
(BytesIndicated == BytesAvailable) &&
|
||
BytesAvailable <= workContext->RequestBuffer->BufferLength ) {
|
||
|
||
TdiCopyLookaheadData(
|
||
workContext->RequestBuffer->Buffer,
|
||
Tsdu,
|
||
BytesIndicated,
|
||
ReceiveFlags
|
||
);
|
||
|
||
#if SRVDBG_STATS2
|
||
IndicationsCopied++;
|
||
#endif
|
||
|
||
//
|
||
// Pretend the transport completed an IRP by doing what
|
||
// the restart routine, which is known to be
|
||
// SrvQueueWorkToFspAtDpcLevel, would do.
|
||
//
|
||
|
||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||
irp->IoStatus.Information = BytesIndicated;
|
||
|
||
irp->Cancel = FALSE;
|
||
|
||
IF_DEBUG(FSD2) {
|
||
KdPrint(( "FSD working on work context 0x%p", workContext ));
|
||
}
|
||
ASSERT( workContext->FsdRestartRoutine == SrvQueueWorkToFspAtDpcLevel );
|
||
|
||
//
|
||
// *** THE FOLLOWING IS COPIED FROM SrvQueueWorkToFspAtDpcLevel.
|
||
//
|
||
// Increment the processing count.
|
||
//
|
||
|
||
workContext->ProcessingCount++;
|
||
|
||
//
|
||
// Insert the work item at the tail of the nonblocking
|
||
// work queue.
|
||
//
|
||
|
||
SrvInsertWorkQueueTail(
|
||
workContext->CurrentWorkQueue,
|
||
(PQUEUEABLE_BLOCK_HEADER)workContext
|
||
);
|
||
|
||
//
|
||
// Tell the transport that we copied the data.
|
||
//
|
||
|
||
*BytesTaken = BytesIndicated;
|
||
*IoRequestPacket = NULL;
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
PTDI_REQUEST_KERNEL_RECEIVE parameters;
|
||
|
||
#if SRVDBG_STATS2
|
||
IndicationsNotCopied++;
|
||
#endif
|
||
|
||
*BytesTaken = 0;
|
||
receiveLength = workContext->RequestBuffer->BufferLength;
|
||
mdl = workContext->RequestBuffer->Mdl;
|
||
|
||
//
|
||
// We can't copy the indicated data. Set up the receive
|
||
// IRP.
|
||
//
|
||
|
||
irp->Tail.Overlay.OriginalFileObject = NULL;
|
||
irp->Tail.Overlay.Thread = queue->IrpThread;
|
||
DEBUG irp->RequestorMode = KernelMode;
|
||
|
||
//
|
||
// Get a pointer to the next stack location. This one is used to
|
||
// hold the parameters for the device I/O control request.
|
||
//
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
|
||
//
|
||
// Set up the completion routine.
|
||
//
|
||
|
||
IoSetCompletionRoutine(
|
||
irp,
|
||
SrvFsdIoCompletionRoutine,
|
||
workContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE
|
||
);
|
||
|
||
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpSp->MinorFunction = (UCHAR)TDI_RECEIVE;
|
||
|
||
//
|
||
// Copy the caller's parameters to the service-specific portion of the
|
||
// IRP for those parameters that are the same for all three methods.
|
||
//
|
||
|
||
parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
|
||
parameters->ReceiveLength = receiveLength;
|
||
parameters->ReceiveFlags = 0;
|
||
|
||
irp->MdlAddress = mdl;
|
||
irp->AssociatedIrp.SystemBuffer = NULL;
|
||
|
||
//
|
||
// Make the next stack location current. Normally
|
||
// IoCallDriver would do this, but since we're bypassing
|
||
// that, we do it directly. Load the target device
|
||
// object address into the stack location. This
|
||
// especially important because the server likes to
|
||
// reuse IRPs.
|
||
//
|
||
|
||
irpSp->Flags = 0;
|
||
irpSp->DeviceObject = connection->DeviceObject;
|
||
irpSp->FileObject = connection->FileObject;
|
||
|
||
IoSetNextIrpStackLocation( irp );
|
||
|
||
ASSERT( irp->StackCount >= irpSp->DeviceObject->StackSize );
|
||
|
||
//
|
||
// Return STATUS_MORE_PROCESSING_REQUIRED so that the
|
||
// transport provider will use our IRP to service the
|
||
// receive.
|
||
//
|
||
|
||
*IoRequestPacket = irp;
|
||
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// No preformatted work items are available. Mark the
|
||
// connection, put it on a queue of connections waiting for
|
||
// work items, and wake up the resource thread so that it
|
||
// can format some more work items and service pended
|
||
// connections.
|
||
//
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvFsdTdiReceiveHandler: no receive work items available",
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
connection->NoResponseSignatureIndex =
|
||
(((PSMB_HEADER)Tsdu)->Command == SMB_COM_NT_CANCEL);
|
||
|
||
//
|
||
// Remember the amount of data available, so we can set it
|
||
// in the workcontext when we eventually find one to use
|
||
//
|
||
connection->BytesAvailable = BytesAvailable;
|
||
|
||
// If we've been under DOS attacks recently, queue a tear-down
|
||
if( SrvDoSWorkItemTearDown > SRV_DOS_TEARDOWN_MIN )
|
||
{
|
||
SrvCheckForAndQueueDoS( queue );
|
||
}
|
||
|
||
(VOID)SrvAddToNeedResourceQueue( connection, ReceivePending, NULL );
|
||
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
}
|
||
|
||
if ( !(ReceiveFlags & TDI_RECEIVE_AT_DISPATCH_LEVEL) ) {
|
||
KeLowerIrql( oldIrql );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The connection is not active. Ignore this message.
|
||
//
|
||
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
} // SrvFsdTdiReceiveHandler
|
||
|
||
VOID
|
||
SrvPnpBindingHandler(
|
||
IN TDI_PNP_OPCODE PnPOpcode,
|
||
IN PUNICODE_STRING DeviceName,
|
||
IN PWSTR MultiSZBindList
|
||
)
|
||
{
|
||
KAPC_STATE ApcState;
|
||
BOOLEAN Attached = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
switch( PnPOpcode ) {
|
||
case TDI_PNP_OP_DEL:
|
||
case TDI_PNP_OP_ADD:
|
||
case TDI_PNP_OP_UPDATE:
|
||
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(("SRV: SrvPnpBindingHandler( %wZ, %u ) entered\n", DeviceName, PnPOpcode ));
|
||
}
|
||
|
||
if( IoGetCurrentProcess() != SrvServerProcess ) {
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(("SRV: attach to system process\n" ));
|
||
}
|
||
FsRtlEnterFileSystem();
|
||
KeStackAttachProcess( SrvServerProcess, &ApcState );
|
||
Attached = TRUE;
|
||
}
|
||
|
||
if ((PnPOpcode == TDI_PNP_OP_DEL) ||
|
||
(PnPOpcode == TDI_PNP_OP_ADD)) {
|
||
SrvXsPnpOperation( DeviceName, (BOOLEAN)(PnPOpcode == TDI_PNP_OP_ADD) );
|
||
}
|
||
|
||
SrvpNotifyChangesToNetBt(PnPOpcode,DeviceName,MultiSZBindList);
|
||
|
||
if( Attached == TRUE ) {
|
||
KeUnstackDetachProcess( &ApcState );
|
||
FsRtlExitFileSystem();
|
||
}
|
||
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(("SRV: SrvPnpBindingHandler( %p, %u ) returning\n", DeviceName, PnPOpcode ));
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// This routine can not be pageable, since the set power state calls can
|
||
// come through with the disk already disabled. We need to make sure that
|
||
// no pageable code is invoked. Fortunately, we don't need to do anything
|
||
// on a set power state
|
||
//
|
||
NTSTATUS
|
||
SrvPnpPowerHandler(
|
||
IN PUNICODE_STRING DeviceName,
|
||
IN PNET_PNP_EVENT PnPEvent,
|
||
IN PTDI_PNP_CONTEXT Context1,
|
||
IN PTDI_PNP_CONTEXT Context2
|
||
)
|
||
{
|
||
NET_DEVICE_POWER_STATE powerState;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(( "SRV: SrvPnpPowerHandler( %wZ, %u )\n", DeviceName, PnPEvent->NetEvent ));
|
||
}
|
||
|
||
switch( PnPEvent->NetEvent ) {
|
||
case NetEventQueryPower:
|
||
|
||
if( PnPEvent->BufferLength != sizeof( powerState ) ) {
|
||
IF_DEBUG( ERRORS ) {
|
||
KdPrint(( "SRV: NetEventQueryPower BufferLength %u (should be %u)\n",
|
||
PnPEvent->BufferLength, sizeof( powerState ) ));
|
||
}
|
||
break;
|
||
}
|
||
|
||
powerState = *(PNET_DEVICE_POWER_STATE)(PnPEvent->Buffer);
|
||
|
||
//
|
||
// Powering up is always OK!
|
||
//
|
||
if( powerState == NetDeviceStateD0 ) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Lack of break is intentional
|
||
//
|
||
case NetEventQueryRemoveDevice:
|
||
|
||
//
|
||
// The following code is disabled, because we couldn't come up with a reasonable
|
||
// way to present UI to the user on failure. We know this leg of the code is only
|
||
// executed when the user specifically wants to standby the system or remove a device.
|
||
// The analogy is that of a TV set -- if the user wants to turn off the system, then who
|
||
// are we to say "no"?
|
||
//
|
||
// If this is really the decision, then IMHO the system shouldn't even ask us. But
|
||
// that's a battle long lost.
|
||
//
|
||
#if 0
|
||
//
|
||
// Run through the endpoints using this device. If any clients are
|
||
// connected, refuse the change.
|
||
//
|
||
|
||
ACQUIRE_LOCK( &SrvEndpointLock );
|
||
|
||
listEntry = SrvEndpointList.ListHead.Flink;
|
||
|
||
while( listEntry != &SrvEndpointList.ListHead ) {
|
||
|
||
PENDPOINT endpoint = CONTAINING_RECORD( listEntry,
|
||
ENDPOINT,
|
||
GlobalEndpointListEntry
|
||
);
|
||
|
||
if( GET_BLOCK_STATE( endpoint ) == BlockStateActive &&
|
||
RtlEqualUnicodeString( DeviceName, &endpoint->TransportName, TRUE ) ) {
|
||
|
||
USHORT index = (USHORT)-1;
|
||
PCONNECTION connection = WalkConnectionTable( endpoint, &index );
|
||
|
||
if( connection != NULL ) {
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint((" Endpoint %X, Connection %X\n", endpoint, connection ));
|
||
KdPrint((" SRV rejects power down request!\n" ));
|
||
}
|
||
//
|
||
// We have found a connected client. Cannot allow powerdown.
|
||
//
|
||
SrvDereferenceConnection( connection );
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
}
|
||
|
||
listEntry = listEntry->Flink;
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvEndpointLock );
|
||
#endif
|
||
|
||
break;
|
||
}
|
||
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(( " SrvPnpPowerHandler status %X\n", status ));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
PWORK_CONTEXT SRVFASTCALL
|
||
SrvFsdGetReceiveWorkItem (
|
||
IN PWORK_QUEUE queue
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function removes a receive work item from the free queue. It can
|
||
be called at either Passive or DPC level
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
PWORK_CONTEXT - A pointer to a WORK_CONTEXT structure,
|
||
or NULL if none exists.
|
||
--*/
|
||
|
||
{
|
||
PSINGLE_LIST_ENTRY listEntry;
|
||
PWORK_CONTEXT workContext;
|
||
ULONG i;
|
||
KIRQL oldIrql;
|
||
NTSTATUS Status;
|
||
BOOLEAN AllocFailed = FALSE;
|
||
|
||
ASSERT( queue >= SrvWorkQueues && queue < eSrvWorkQueues );
|
||
|
||
//
|
||
// Try to get a work context block from the initial work queue first.
|
||
// If this fails, try the normal work queue. If this fails, try to allocate
|
||
// one. If we still failed, schedule a worker thread to allocate some later.
|
||
//
|
||
|
||
listEntry = ExInterlockedPopEntrySList( &queue->InitialWorkItemList, &queue->SpinLock );
|
||
|
||
if ( listEntry == NULL ) {
|
||
|
||
listEntry = ExInterlockedPopEntrySList( &queue->NormalWorkItemList, &queue->SpinLock );
|
||
|
||
if( listEntry == NULL ) {
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("No workitems for queue %p\n", (PVOID)(queue-SrvWorkQueues) ));
|
||
}
|
||
|
||
Status = SrvAllocateNormalWorkItem( &workContext, queue );
|
||
if( workContext != NULL ) {
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("SrvFsdGetReceiveWorkItem: new work context %p\n",
|
||
workContext ));
|
||
}
|
||
SrvPrepareReceiveWorkItem( workContext, FALSE );
|
||
INITIALIZE_WORK_CONTEXT( queue, workContext );
|
||
return workContext;
|
||
}
|
||
else
|
||
{
|
||
if( Status == STATUS_INSUFFICIENT_RESOURCES )
|
||
{
|
||
AllocFailed = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Before we steal from another processor, ensure that
|
||
// we're setup to replenish this exhausted free list
|
||
//
|
||
ACQUIRE_SPIN_LOCK( &queue->SpinLock, &oldIrql );
|
||
if( queue->AllocatedWorkItems < queue->MaximumWorkItems &&
|
||
GET_BLOCK_TYPE(&queue->CreateMoreWorkItems) == BlockTypeGarbage ) {
|
||
|
||
SET_BLOCK_TYPE( &queue->CreateMoreWorkItems, BlockTypeWorkContextSpecial );
|
||
queue->CreateMoreWorkItems.FspRestartRoutine = SrvServiceWorkItemShortage;
|
||
SrvInsertWorkQueueHead( queue, &queue->CreateMoreWorkItems );
|
||
}
|
||
RELEASE_SPIN_LOCK( &queue->SpinLock, oldIrql );
|
||
|
||
#if MULTIPROCESSOR
|
||
//
|
||
// We couldn't find a work item on our processor's free queue.
|
||
// See if we can steal one from another processor
|
||
//
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("Looking for workitems on other processors\n" ));
|
||
}
|
||
|
||
//
|
||
// Look around for a workitem we can borrow
|
||
//
|
||
for( i = SrvNumberOfProcessors; i > 1; --i ) {
|
||
|
||
if( ++queue == eSrvWorkQueues )
|
||
queue = SrvWorkQueues;
|
||
|
||
|
||
listEntry = ExInterlockedPopEntrySList( &queue->InitialWorkItemList,
|
||
&queue->SpinLock );
|
||
|
||
if( listEntry == NULL ) {
|
||
listEntry = ExInterlockedPopEntrySList( &queue->NormalWorkItemList,
|
||
&queue->SpinLock );
|
||
if( listEntry == NULL ) {
|
||
|
||
Status = SrvAllocateNormalWorkItem( &workContext, queue );
|
||
|
||
if( workContext != NULL ) {
|
||
//
|
||
// Got a workItem from another processor's queue!
|
||
//
|
||
++(queue->StolenWorkItems);
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("SrvFsdGetReceiveWorkItem: new work context %p\n",
|
||
workContext ));
|
||
}
|
||
|
||
SrvPrepareReceiveWorkItem( workContext, FALSE );
|
||
INITIALIZE_WORK_CONTEXT( queue, workContext );
|
||
return workContext;
|
||
}
|
||
else
|
||
{
|
||
if( Status == STATUS_INSUFFICIENT_RESOURCES )
|
||
{
|
||
AllocFailed = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure this processor knows it is low on workitems
|
||
//
|
||
ACQUIRE_SPIN_LOCK( &queue->SpinLock, &oldIrql );
|
||
if( queue->AllocatedWorkItems < queue->MaximumWorkItems &&
|
||
GET_BLOCK_TYPE(&queue->CreateMoreWorkItems) == BlockTypeGarbage ) {
|
||
|
||
SET_BLOCK_TYPE( &queue->CreateMoreWorkItems,
|
||
BlockTypeWorkContextSpecial );
|
||
|
||
queue->CreateMoreWorkItems.FspRestartRoutine
|
||
= SrvServiceWorkItemShortage;
|
||
SrvInsertWorkQueueHead( queue, &queue->CreateMoreWorkItems );
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &queue->SpinLock, oldIrql );
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Got a workItem from another processor's queue!
|
||
//
|
||
++(queue->StolenWorkItems);
|
||
|
||
break;
|
||
}
|
||
#endif
|
||
|
||
if( listEntry == NULL ) {
|
||
//
|
||
// We didn't have any free workitems on our queue, and
|
||
// we couldn't borrow a workitem from another processor.
|
||
// Give up!
|
||
//
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("No workitems anywhere!\n" ));
|
||
}
|
||
++SrvStatistics.WorkItemShortages;
|
||
|
||
if( !AllocFailed )
|
||
{
|
||
SrvCheckForAndQueueDoS( queue );
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// We've successfully gotten a free workitem of a processor's queue.
|
||
// (it may not be our processor).
|
||
//
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
if( queue != PROCESSOR_TO_QUEUE() ) {
|
||
KdPrint(("\tGot WORK_ITEM from processor %p\n" , (PVOID)(queue - SrvWorkQueues) ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Decrement the count of free receive work items.
|
||
//
|
||
InterlockedDecrement( &queue->FreeWorkItems );
|
||
|
||
if( queue->FreeWorkItems < queue->MinFreeWorkItems &&
|
||
queue->AllocatedWorkItems < queue->MaximumWorkItems &&
|
||
GET_BLOCK_TYPE(&queue->CreateMoreWorkItems) == BlockTypeGarbage ) {
|
||
|
||
ACQUIRE_SPIN_LOCK( &queue->SpinLock, &oldIrql );
|
||
|
||
if( queue->FreeWorkItems < queue->MinFreeWorkItems &&
|
||
queue->AllocatedWorkItems < queue->MaximumWorkItems &&
|
||
GET_BLOCK_TYPE(&queue->CreateMoreWorkItems) == BlockTypeGarbage ) {
|
||
|
||
//
|
||
// We're running short of free workitems. Queue a request to
|
||
// allocate more of them.
|
||
//
|
||
SET_BLOCK_TYPE( &queue->CreateMoreWorkItems, BlockTypeWorkContextSpecial );
|
||
|
||
queue->CreateMoreWorkItems.FspRestartRoutine = SrvServiceWorkItemShortage;
|
||
SrvInsertWorkQueueHead( queue, &queue->CreateMoreWorkItems );
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &queue->SpinLock, oldIrql );
|
||
}
|
||
|
||
|
||
workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry );
|
||
ASSERT( workContext->BlockHeader.ReferenceCount == 0 );
|
||
ASSERT( workContext->CurrentWorkQueue != NULL );
|
||
|
||
INITIALIZE_WORK_CONTEXT( queue, workContext );
|
||
|
||
return workContext;
|
||
|
||
} // SrvFsdGetReceiveWorkItem
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRequeueReceiveWorkItem (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine requeues a Receive work item to the queue in the server
|
||
FSD device object. This routine is called when processing of a
|
||
receive work item is done.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block associated
|
||
with the receive buffer and IRP. The following fields must be
|
||
valid:
|
||
|
||
Connection
|
||
TdiRequest
|
||
Irp
|
||
RequestBuffer
|
||
RequestBuffer->BufferLength
|
||
RequestBuffer->Mdl
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION connection;
|
||
PSMB_HEADER header;
|
||
KIRQL oldIrql;
|
||
PBUFFER requestBuffer;
|
||
|
||
IF_DEBUG(TRACE2) KdPrint(( "SrvFsdRequeueReceiveWorkItem entered\n" ));
|
||
IF_DEBUG(NET2) {
|
||
KdPrint(( " Work context %p, request buffer %p\n",
|
||
WorkContext, WorkContext->RequestBuffer ));
|
||
KdPrint(( " IRP %p, MDL %p\n",
|
||
WorkContext->Irp, WorkContext->RequestBuffer->Mdl ));
|
||
}
|
||
|
||
//
|
||
// Save the connection pointer before reinitializing the work item.
|
||
//
|
||
|
||
connection = WorkContext->Connection;
|
||
ASSERT( connection != NULL );
|
||
|
||
ASSERT( WorkContext->Share == NULL );
|
||
ASSERT( WorkContext->Session == NULL );
|
||
ASSERT( WorkContext->TreeConnect == NULL );
|
||
ASSERT( WorkContext->Rfcb == NULL );
|
||
|
||
//
|
||
// Reset the IRP cancelled bit, in case it was set during
|
||
// operation.
|
||
//
|
||
|
||
WorkContext->Irp->Cancel = FALSE;
|
||
|
||
//
|
||
// Set up the restart routine in the work context.
|
||
//
|
||
|
||
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
||
WorkContext->FspRestartRoutine = SrvRestartReceive;
|
||
|
||
//
|
||
// Make sure the length specified in the MDL is correct -- it may
|
||
// have changed while sending a response to the previous request.
|
||
// Call an I/O subsystem routine to build the I/O request packet.
|
||
//
|
||
|
||
requestBuffer = WorkContext->RequestBuffer;
|
||
requestBuffer->Mdl->ByteCount = requestBuffer->BufferLength;
|
||
|
||
//
|
||
// Replace the Response buffer.
|
||
//
|
||
|
||
WorkContext->ResponseBuffer = requestBuffer;
|
||
|
||
header = (PSMB_HEADER)requestBuffer->Buffer;
|
||
|
||
//WorkContext->RequestHeader = header;
|
||
ASSERT( WorkContext->RequestHeader == header );
|
||
|
||
WorkContext->ResponseHeader = header;
|
||
WorkContext->ResponseParameters = (PVOID)(header + 1);
|
||
WorkContext->RequestParameters = (PVOID)(header + 1);
|
||
|
||
//
|
||
// Initialize this to zero so this will not be mistakenly cancelled
|
||
// by SrvSmbNtCancel.
|
||
//
|
||
|
||
SmbPutAlignedUshort( &WorkContext->RequestHeader->Uid, (USHORT)0 );
|
||
|
||
//
|
||
// Remove the work item from the in-progress list.
|
||
//
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
SrvRemoveEntryList(
|
||
&connection->InProgressWorkItemList,
|
||
&WorkContext->InProgressListEntry
|
||
);
|
||
connection->InProgressWorkContextCount--;
|
||
|
||
//
|
||
// Attempt to dereference the connection.
|
||
//
|
||
|
||
if ( --connection->BlockHeader.ReferenceCount == 0 ) {
|
||
|
||
//
|
||
// The refcount went to zero. We can't handle this with the
|
||
// spin lock held. Reset the refcount, then release the lock,
|
||
// then check to see whether we can continue here.
|
||
//
|
||
|
||
connection->BlockHeader.ReferenceCount++;
|
||
|
||
//
|
||
// We're in the FSD, so we can't do this here. We need
|
||
// to tell our caller this.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
//
|
||
// orphaned. Send to Boys Town.
|
||
//
|
||
|
||
DispatchToOrphanage( (PVOID)connection );
|
||
connection = NULL;
|
||
|
||
} else {
|
||
|
||
UPDATE_REFERENCE_HISTORY( connection, TRUE );
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
}
|
||
|
||
KeLowerIrql( oldIrql );
|
||
|
||
//
|
||
// Requeue the work item.
|
||
//
|
||
|
||
RETURN_FREE_WORKITEM( WorkContext );
|
||
|
||
return;
|
||
|
||
} // SrvFsdRequeueReceiveWorkItem
|
||
|
||
|
||
NTSTATUS
|
||
SrvFsdRestartSendOplockIItoNone(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the send completion routine for oplock breaks from
|
||
II to None. This is handled differently from other oplock breaks
|
||
in that we don't queue it to the OplockBreaksInProgressList but
|
||
increment our count so we will not have raw reads while this is
|
||
being sent.
|
||
|
||
|
||
This is done in such a manner as to be safe at DPC level.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
WorkContext - Caller-specified context parameter associated with IRP.
|
||
This is actually a pointer to a Work Context block.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
PCONNECTION connection;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
IF_DEBUG(OPLOCK) {
|
||
KdPrint(("SrvFsdRestartSendOplockIItoNone: Oplock send complete.\n"));
|
||
}
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Reset the IRP cancelled bit.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
|
||
//
|
||
// Mark the connection to indicate that we just sent a break II to
|
||
// none. If the next SMB received is a raw read, we will return a
|
||
// zero-byte send. This is necessary because the client doesn't
|
||
// respond to this break, so we don't really know when they've
|
||
// received it. But when we receive an SMB, we know that they've
|
||
// gotten it. Note that a non-raw SMB could be on its way when we
|
||
// send the break, and we'll clear the flag, but since the client
|
||
// needs to lock the VC to do the raw read, it must receive the SMB
|
||
// response (and hence the oplock break) before it can send the raw
|
||
// read. If a raw read crosses with the oplock break, it will be
|
||
// rejected because the OplockBreaksInProgress count is nonzero.
|
||
//
|
||
|
||
connection = WorkContext->Connection;
|
||
connection->BreakIIToNoneJustSent = TRUE;
|
||
|
||
ExInterlockedAddUlong(
|
||
&connection->OplockBreaksInProgress,
|
||
(ULONG)-1,
|
||
connection->EndpointSpinLock
|
||
);
|
||
|
||
SrvFsdRestartSmbComplete( WorkContext );
|
||
|
||
KeLowerIrql( oldIrql );
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
} // SrvFsdRestartSendOplockIItoNone
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartSmbComplete (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when all request processing on an SMB is
|
||
complete, including sending a response, if any. This routine
|
||
dereferences control blocks and requeues the work item to the
|
||
receive work item list.
|
||
|
||
This is done in such a manner as to be safe at DPC level.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
containing information about the SMB.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRFCB rfcb;
|
||
ULONG oldCount;
|
||
|
||
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
||
|
||
IF_DEBUG(FSD2) KdPrint(( "SrvFsdRestartSmbComplete entered\n" ));
|
||
|
||
//
|
||
// If we may have backlogged oplock breaks to send, do the work
|
||
// in the FSP.
|
||
//
|
||
|
||
if ( WorkContext->OplockOpen ) {
|
||
goto queueToFsp;
|
||
}
|
||
|
||
//
|
||
// Attempt to dereference the file block.
|
||
//
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
|
||
if ( rfcb != NULL ) {
|
||
oldCount = ExInterlockedAddUlong(
|
||
&rfcb->BlockHeader.ReferenceCount,
|
||
(ULONG)-1,
|
||
&rfcb->Connection->SpinLock
|
||
);
|
||
|
||
UPDATE_REFERENCE_HISTORY( rfcb, TRUE );
|
||
|
||
if ( oldCount == 1 ) {
|
||
UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
|
||
(VOID) ExInterlockedAddUlong(
|
||
&rfcb->BlockHeader.ReferenceCount,
|
||
(ULONG) 1,
|
||
&rfcb->Connection->SpinLock
|
||
);
|
||
goto queueToFsp;
|
||
}
|
||
|
||
WorkContext->Rfcb = NULL;
|
||
}
|
||
|
||
//
|
||
// If this was a blocking operation, update the blocking i/o count.
|
||
//
|
||
|
||
if ( WorkContext->BlockingOperation ) {
|
||
InterlockedDecrement( &SrvBlockingOpsInProgress );
|
||
WorkContext->BlockingOperation = FALSE;
|
||
}
|
||
|
||
//
|
||
// !!! Need to handle failure of response send -- kill connection?
|
||
//
|
||
|
||
//
|
||
// Attempt to dereference the work item. This will fail (and
|
||
// automatically be queued to the FSP) if it cannot be done from
|
||
// within the FSD.
|
||
//
|
||
|
||
SrvFsdDereferenceWorkItem( WorkContext );
|
||
|
||
return;
|
||
|
||
queueToFsp:
|
||
|
||
//
|
||
// We were unable to do all the necessary cleanup at DPC level.
|
||
// Queue the work item to the FSP.
|
||
//
|
||
|
||
WorkContext->FspRestartRoutine = SrvRestartFsdComplete;
|
||
SrvQueueWorkToFspAtDpcLevel( WorkContext );
|
||
|
||
IF_DEBUG(FSD2) KdPrint(( "SrvFsdRestartSmbComplete complete\n" ));
|
||
return;
|
||
|
||
} // SrvFsdRestartSmbComplete
|
||
|
||
NTSTATUS
|
||
SrvFsdRestartSmbAtSendCompletion (
|
||
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
||
IN PIRP Irp,
|
||
IN PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send completion routine for all request processing on an SMB is
|
||
complete, including sending a response, if any. This routine
|
||
dereferences control blocks and requeues the work item to the
|
||
receive work item list.
|
||
|
||
This is done in such a manner as to be safe at DPC level.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
WorkContext - Caller-specified context parameter associated with IRP.
|
||
This is actually a pointer to a Work Context block.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRFCB rfcb;
|
||
KIRQL oldIrql;
|
||
|
||
ULONG oldCount;
|
||
|
||
IF_DEBUG(FSD2)KdPrint(( "SrvFsdRestartSmbComplete entered\n" ));
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Reset the IRP cancelled bit.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
//
|
||
// Free any associated buffers
|
||
//
|
||
if( Irp->AssociatedIrp.SystemBuffer != NULL &&
|
||
(Irp->Flags & IRP_DEALLOCATE_BUFFER) ) {
|
||
|
||
ExFreePool( Irp->AssociatedIrp.SystemBuffer );
|
||
Irp->AssociatedIrp.SystemBuffer = NULL;
|
||
Irp->Flags &= ~IRP_DEALLOCATE_BUFFER;
|
||
}
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
|
||
//
|
||
// If we may have backlogged oplock breaks to send, do the work
|
||
// in the FSP.
|
||
//
|
||
|
||
if ( WorkContext->OplockOpen ) {
|
||
goto queueToFsp;
|
||
}
|
||
|
||
//
|
||
// Attempt to dereference the file block. We can do this without acquiring
|
||
// SrvFsdSpinlock around the decrement/increment pair since the ref
|
||
// count cannot be zero unless the rfcb is closed and we are the last
|
||
// reference to it. None of our code references the rfcb when it has been
|
||
// closed.
|
||
//
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
|
||
if ( rfcb != NULL ) {
|
||
oldCount = ExInterlockedAddUlong(
|
||
&rfcb->BlockHeader.ReferenceCount,
|
||
(ULONG)-1,
|
||
&rfcb->Connection->SpinLock
|
||
);
|
||
|
||
UPDATE_REFERENCE_HISTORY( rfcb, TRUE );
|
||
|
||
if ( oldCount == 1 ) {
|
||
UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
|
||
(VOID) ExInterlockedAddUlong(
|
||
&rfcb->BlockHeader.ReferenceCount,
|
||
(ULONG) 1,
|
||
&rfcb->Connection->SpinLock
|
||
);
|
||
goto queueToFsp;
|
||
}
|
||
|
||
WorkContext->Rfcb = NULL;
|
||
}
|
||
|
||
//
|
||
// If this was a blocking operation, update the blocking i/o count.
|
||
//
|
||
|
||
if ( WorkContext->BlockingOperation ) {
|
||
InterlockedDecrement( &SrvBlockingOpsInProgress );
|
||
WorkContext->BlockingOperation = FALSE;
|
||
}
|
||
|
||
//
|
||
// !!! Need to handle failure of response send -- kill connection?
|
||
//
|
||
|
||
//
|
||
// Attempt to dereference the work item. This will fail (and
|
||
// automatically be queued to the FSP) if it cannot be done from
|
||
// within the FSD.
|
||
//
|
||
|
||
SrvFsdDereferenceWorkItem( WorkContext );
|
||
|
||
KeLowerIrql( oldIrql );
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
queueToFsp:
|
||
|
||
//
|
||
// We were unable to do all the necessary cleanup at DPC level.
|
||
// Queue the work item to the FSP.
|
||
//
|
||
|
||
WorkContext->FspRestartRoutine = SrvRestartFsdComplete;
|
||
SrvQueueWorkToFspAtDpcLevel( WorkContext );
|
||
|
||
KeLowerIrql( oldIrql );
|
||
IF_DEBUG(FSD2) KdPrint(( "SrvFsdRestartSmbComplete complete\n" ));
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
} // SrvFsdRestartSmbAtSendCompletion
|
||
|
||
|
||
VOID
|
||
SrvFsdServiceNeedResourceQueue (
|
||
IN PWORK_CONTEXT *WorkContext,
|
||
IN PKIRQL OldIrql
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function attempts to service a receive pending by creating
|
||
a new SMB buffer and passing it on to the transport provider.
|
||
|
||
*** SrvFsdSpinLock held when called. Held on exit ***
|
||
*** EndpointSpinLock held when called. Held on exit ***
|
||
|
||
Arguments:
|
||
|
||
WorkContext - A pointer to the work context block that will be used
|
||
to service the connection. If the work context supplied
|
||
was used, a null will be returned here.
|
||
*** The workcontext block must be referencing the
|
||
connection on entry. ***
|
||
|
||
OldIrql -
|
||
|
||
Return Value:
|
||
|
||
TRUE, if there is still work left for this connection.
|
||
FALSE, otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp;
|
||
PIRP irp;
|
||
PWORK_CONTEXT workContext = *WorkContext;
|
||
PCONNECTION connection = workContext->Connection;
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: entered. WorkContext %p , Connection = %p.\n", workContext, connection ));
|
||
}
|
||
|
||
//
|
||
// If there are any oplock break sends pending, supply the WCB.
|
||
//
|
||
|
||
restart:
|
||
|
||
if ( !IsListEmpty( &connection->OplockWorkList ) ) {
|
||
|
||
PLIST_ENTRY listEntry;
|
||
PRFCB rfcb;
|
||
|
||
//
|
||
// Dequeue the oplock context from the list of pending oplock
|
||
// sends.
|
||
//
|
||
|
||
listEntry = RemoveHeadList( &connection->OplockWorkList );
|
||
|
||
rfcb = CONTAINING_RECORD( listEntry, RFCB, ListEntry );
|
||
|
||
#if DBG
|
||
rfcb->ListEntry.Flink = rfcb->ListEntry.Blink = NULL;
|
||
#endif
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: rfcb %p removed from OplockWorkList.\n", rfcb ));
|
||
}
|
||
|
||
//
|
||
// The connection spinlock guards the rfcb block header.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock);
|
||
|
||
if ( GET_BLOCK_STATE( rfcb ) != BlockStateActive ) {
|
||
|
||
//
|
||
// This file is closing, don't bother send the oplock break
|
||
//
|
||
// Attempt to dereference the file block.
|
||
//
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: rfcb %p closing.\n", rfcb));
|
||
}
|
||
|
||
UPDATE_REFERENCE_HISTORY( rfcb, TRUE );
|
||
|
||
connection->OplockBreaksInProgress--;
|
||
if ( --rfcb->BlockHeader.ReferenceCount == 0 ) {
|
||
|
||
//
|
||
// Put the work item on the in-progress list.
|
||
//
|
||
|
||
SrvInsertTailList(
|
||
&connection->InProgressWorkItemList,
|
||
&workContext->InProgressListEntry
|
||
);
|
||
connection->InProgressWorkContextCount++;
|
||
|
||
UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
|
||
rfcb->BlockHeader.ReferenceCount++;
|
||
|
||
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock);
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock);
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, *OldIrql );
|
||
|
||
//
|
||
// Send this to the Fsp
|
||
//
|
||
|
||
workContext->Rfcb = rfcb;
|
||
workContext->FspRestartRoutine = SrvRestartFsdComplete;
|
||
SrvQueueWorkToFsp( workContext );
|
||
goto exit_used;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Before we get rid of the workcontext block, see
|
||
// if we need to do more work for this connection.
|
||
//
|
||
|
||
if ( !IsListEmpty(&connection->OplockWorkList) ||
|
||
connection->ReceivePending) {
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: Reusing WorkContext block %p.\n", workContext ));
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock);
|
||
goto restart;
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock);
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock);
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, *OldIrql );
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: WorkContext block not used.\n"));
|
||
}
|
||
|
||
SrvDereferenceConnection( connection );
|
||
workContext->Connection = NULL;
|
||
workContext->Endpoint = NULL;
|
||
goto exit_not_used;
|
||
}
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock);
|
||
|
||
//
|
||
// Put the work item on the in-progress list.
|
||
//
|
||
|
||
SrvInsertTailList(
|
||
&connection->InProgressWorkItemList,
|
||
&workContext->InProgressListEntry
|
||
);
|
||
connection->InProgressWorkContextCount++;
|
||
|
||
//
|
||
// Copy the oplock work queue RFCB reference to the work
|
||
// context block. There is no need to re-reference the RFCB.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock);
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, *OldIrql );
|
||
workContext->Rfcb = rfcb;
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: Sending oplock break.\n"));
|
||
}
|
||
|
||
SrvRestartOplockBreakSend( workContext );
|
||
|
||
} else {
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: Have ReceivePending.\n"));
|
||
}
|
||
|
||
//
|
||
// Offer the newly free, or newly created, SMB buffer to the
|
||
// transport to complete the receive.
|
||
//
|
||
// *** Note that the connection has already been referenced in
|
||
// SrvFsdTdiReceiveHandler.
|
||
//
|
||
|
||
connection->ReceivePending = FALSE;
|
||
|
||
//
|
||
// Check the request security signature, and calculate the response signature
|
||
//
|
||
if( connection->SmbSecuritySignatureActive ) {
|
||
|
||
//
|
||
// Save this security signature index
|
||
//
|
||
workContext->SmbSecuritySignatureIndex =
|
||
connection->SmbSecuritySignatureIndex++;
|
||
|
||
//
|
||
// Save the response signature index, if we require one
|
||
//
|
||
if( connection->NoResponseSignatureIndex == FALSE ) {
|
||
|
||
workContext->ResponseSmbSecuritySignatureIndex =
|
||
connection->SmbSecuritySignatureIndex++;
|
||
}
|
||
}
|
||
|
||
SET_OPERATION_START_TIME( &workContext );
|
||
|
||
//
|
||
// Put the work item on the in-progress list.
|
||
//
|
||
|
||
SrvInsertTailList(
|
||
&connection->InProgressWorkItemList,
|
||
&workContext->InProgressListEntry
|
||
);
|
||
connection->InProgressWorkContextCount++;
|
||
|
||
//
|
||
// Remember the amount of data that is avilable, in case it
|
||
// turns out to be a LargeIndication.
|
||
//
|
||
workContext->BytesAvailable = connection->BytesAvailable;
|
||
|
||
//
|
||
// Finish setting up the IRP. This involves putting the
|
||
// file object and device object addresses in the IRP.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock);
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, *OldIrql );
|
||
|
||
irp = workContext->Irp;
|
||
|
||
//
|
||
// Build the receive irp
|
||
//
|
||
|
||
(VOID)SrvBuildIoControlRequest(
|
||
irp, // input IRP address
|
||
NULL, // target file object address
|
||
workContext, // context
|
||
IRP_MJ_INTERNAL_DEVICE_CONTROL, // major function
|
||
TDI_RECEIVE, // minor function
|
||
NULL, // input buffer address
|
||
0, // input buffer length
|
||
workContext->RequestBuffer->Buffer, // output buffer address
|
||
workContext->RequestBuffer->BufferLength, // output buffer length
|
||
workContext->RequestBuffer->Mdl, // MDL address
|
||
NULL // completion routine
|
||
);
|
||
|
||
//
|
||
// Get a pointer to the next stack location. This one is used to
|
||
// hold the parameters for the receive request.
|
||
//
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
|
||
irpSp->Flags = 0;
|
||
irpSp->DeviceObject = connection->DeviceObject;
|
||
irpSp->FileObject = connection->FileObject;
|
||
|
||
ASSERT( irp->StackCount >= irpSp->DeviceObject->StackSize );
|
||
|
||
//
|
||
// Pass the SMB buffer to the driver.
|
||
//
|
||
|
||
IoCallDriver( irpSp->DeviceObject, irp );
|
||
|
||
}
|
||
|
||
exit_used:
|
||
|
||
//
|
||
// We made good use of the work context block.
|
||
//
|
||
|
||
*WorkContext = NULL;
|
||
|
||
IF_DEBUG( OPLOCK ) {
|
||
KdPrint(("SrvFsdServiceNeedResourceQueue: WorkContext block used.\n"));
|
||
}
|
||
|
||
exit_not_used:
|
||
|
||
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, OldIrql );
|
||
ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock);
|
||
return;
|
||
|
||
} // SrvFsdServiceNeedResourceQueue
|
||
|
||
|
||
BOOLEAN
|
||
SrvAddToNeedResourceQueue(
|
||
IN PCONNECTION Connection,
|
||
IN RESOURCE_TYPE ResourceType,
|
||
IN PRFCB Rfcb OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function appends a connection to the need resource queue.
|
||
The connection is marked indicating what resource is needed,
|
||
and the resource thread is started to do the work.
|
||
|
||
Arguments:
|
||
|
||
Connection - The connection that need a resource.
|
||
Resource - The resource that is needed.
|
||
Rfcb - A pointer to the RFCB that needs the resource.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
|
||
ACQUIRE_DPC_SPIN_LOCK( Connection->EndpointSpinLock );
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("SrvAddToNeedResourceQueue entered. connection = %p, type %d\n", Connection, ResourceType));
|
||
}
|
||
|
||
//
|
||
// Check again to see if the connection is closing. If it is,
|
||
// don't bother putting it on the need resource queue.
|
||
//
|
||
// *** We have to do this while holding the need-resource queue
|
||
// spin lock in order to synchronize with
|
||
// SrvCloseConnection. We don't want to queue this
|
||
// connection after SrvCloseConnection tries to take it off
|
||
// the queue.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE(Connection) != BlockStateActive ||
|
||
Connection->DisconnectPending ) {
|
||
|
||
RELEASE_DPC_SPIN_LOCK( Connection->EndpointSpinLock );
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("SrvAddToNeedResourceQueue: connection closing. Not queued\n"));
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Mark the connection so that the resource thread will know what to
|
||
// do with this connection.
|
||
//
|
||
|
||
switch ( ResourceType ) {
|
||
|
||
case ReceivePending:
|
||
|
||
ASSERT( !Connection->ReceivePending );
|
||
Connection->ReceivePending = TRUE;
|
||
break;
|
||
|
||
case OplockSendPending:
|
||
|
||
//
|
||
// Queue the context information to the connection, if necessary.
|
||
//
|
||
|
||
ASSERT( ARGUMENT_PRESENT( Rfcb ) );
|
||
|
||
SrvInsertTailList( &Connection->OplockWorkList, &Rfcb->ListEntry );
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Put the connection on the need-resource queue and increment its
|
||
// reference count.
|
||
//
|
||
|
||
if( Connection->OnNeedResourceQueue == FALSE ) {
|
||
|
||
Connection->OnNeedResourceQueue = TRUE;
|
||
|
||
SrvInsertTailList(
|
||
&SrvNeedResourceQueue,
|
||
&Connection->ListEntry
|
||
);
|
||
|
||
SrvReferenceConnectionLocked( Connection );
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("SrvAddToNeedResourceQueue: connection %p inserted on the queue.\n", Connection));
|
||
}
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK( Connection->EndpointSpinLock );
|
||
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
||
|
||
//
|
||
// Make sure we know that this connection needs a WorkItem
|
||
//
|
||
InterlockedIncrement( &Connection->CurrentWorkQueue->NeedWorkItem );
|
||
|
||
IF_DEBUG( WORKITEMS ) {
|
||
KdPrint(("SrvAddToNeedResourceQueue complete: NeedWorkItem = %d\n",
|
||
Connection->CurrentWorkQueue->NeedWorkItem ));
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
} // SrvAddToNeedResourceQueue
|
||
|
||
VOID
|
||
SrvCheckForAndQueueDoS(
|
||
PWORK_QUEUE queue
|
||
)
|
||
{
|
||
KIRQL oldIrql;
|
||
LARGE_INTEGER CurrentTime;
|
||
BOOLEAN LogEvent = FALSE;
|
||
|
||
if( !SrvDisableDoSChecking &&
|
||
queue->AllocatedWorkItems >= queue->MaximumWorkItems )
|
||
{
|
||
// Potential DoS
|
||
SrvDoSDetected = TRUE;
|
||
|
||
// Make sure we only queue one of these at a time globally
|
||
if( SRV_DOS_CAN_START_TEARDOWN() )
|
||
{
|
||
|
||
KeQuerySystemTime( &CurrentTime );
|
||
|
||
if( CurrentTime.QuadPart > SrvDoSLastRan.QuadPart + SRV_DOS_MINIMUM_DOS_WAIT_PERIOD )
|
||
{
|
||
// Setup a refresher/DOS item
|
||
ACQUIRE_SPIN_LOCK( &queue->SpinLock, &oldIrql );
|
||
ACQUIRE_DPC_SPIN_LOCK( &SrvDosSpinLock );
|
||
|
||
if( GET_BLOCK_TYPE(&SrvDoSWorkItem) == BlockTypeGarbage ) {
|
||
|
||
SET_BLOCK_TYPE( &SrvDoSWorkItem, BlockTypeWorkContextSpecial );
|
||
|
||
if( SrvDoSLastRan.QuadPart + SRV_ONE_DAY < CurrentTime.QuadPart )
|
||
{
|
||
// Only log events when there has been no DoS for 24 hours
|
||
LogEvent = TRUE;
|
||
}
|
||
|
||
SrvDoSLastRan = CurrentTime;
|
||
SrvDoSWorkItem.FspRestartRoutine = SrvServiceDoSTearDown;
|
||
SrvDoSWorkItem.CurrentWorkQueue = queue;
|
||
SrvInsertWorkQueueHead( queue, &SrvDoSWorkItem );
|
||
}
|
||
else
|
||
{
|
||
// Some error occurred, leave the DoS for another queue
|
||
SRV_DOS_COMPLETE_TEARDOWN();
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK( &SrvDosSpinLock );
|
||
RELEASE_SPIN_LOCK( &queue->SpinLock, oldIrql );
|
||
}
|
||
else
|
||
{
|
||
SRV_DOS_COMPLETE_TEARDOWN();
|
||
}
|
||
|
||
// Log the event if necessary
|
||
if( LogEvent )
|
||
{
|
||
SrvLogError(
|
||
SrvDeviceObject,
|
||
EVENT_SRV_OUT_OF_WORK_ITEM_DOS,
|
||
STATUS_ACCESS_DENIED,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|