|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
dispatch.c
Abstract:
Dispatch routines for the Cluster Network Driver.
Author:
Mike Massa (mikemas) January 3, 1997
Revision History:
Who When What -------- -------- ---------------------------------------------- mikemas 01-03-97 created
Notes:
--*/
#include "precomp.h"
#pragma hdrstop
#include "dispatch.tmh"
#include <strsafe.h>
//
// Data
//
PCN_FSCONTEXT CnExclusiveChannel = NULL;
//
// Un-exported Prototypes
//
NTSYSAPI NTSTATUS NTAPI ZwOpenProcess ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL );
//
// Local Prototypes
//
FILE_FULL_EA_INFORMATION UNALIGNED * CnFindEA( PFILE_FULL_EA_INFORMATION StartEA, CHAR *TargetName, USHORT TargetNameLength );
NTSTATUS CnCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp );
NTSTATUS CnCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp );
NTSTATUS CnClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp );
NTSTATUS CnEnableShutdownOnClose( IN PIRP Irp );
BOOLEAN CnPerformSecurityCheck( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp, OUT PNTSTATUS Status );
//
// Mark pageable code.
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, CnDispatchDeviceControl)
#pragma alloc_text(PAGE, CnFindEA)
#pragma alloc_text(PAGE, CnCreate)
#pragma alloc_text(PAGE, CnEnableShutdownOnClose)
#pragma alloc_text(PAGE, CnPerformSecurityCheck)
#endif // ALLOC_PRAGMA
//
// Function definitions
//
VOID CnDereferenceFsContext( PCN_FSCONTEXT FsContext ) { LONG newValue = InterlockedDecrement(&(FsContext->ReferenceCount));
CnAssert(newValue >= 0);
if (newValue != 0) { return; }
//
// Set the cleanup event.
//
KeSetEvent(&(FsContext->CleanupEvent), 0, FALSE);
return;
} // CnDereferenceFsContext
NTSTATUS CnMarkRequestPending( PIRP Irp, PIO_STACK_LOCATION IrpSp, PDRIVER_CANCEL CancelRoutine ) /*++
Notes:
Called with IoCancelSpinLock held.
--*/ { PCN_FSCONTEXT fsContext = (PCN_FSCONTEXT) IrpSp->FileObject->FsContext; CN_IRQL oldIrql;
//
// Set up for cancellation
//
CnAssert(Irp->CancelRoutine == NULL);
if (!Irp->Cancel) {
IoMarkIrpPending(Irp); IoSetCancelRoutine(Irp, CancelRoutine);
CnReferenceFsContext(fsContext);
IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(( "[Clusnet] Pending irp %p fileobj %p.\n", Irp, IrpSp->FileObject )); }
return(STATUS_SUCCESS); }
//
// The IRP has already been cancelled.
//
IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(("[Clusnet] irp %p already cancelled.\n", Irp)); }
return(STATUS_CANCELLED);
} // CnPrepareIrpForCancel
VOID CnCompletePendingRequest( IN PIRP Irp, IN NTSTATUS Status, IN ULONG BytesReturned ) /*++
Routine Description:
Completes a pending request.
Arguments:
Irp - A pointer to the IRP for this request. Status - The final status of the request. BytesReturned - Bytes sent/received information.
Return Value:
None.
Notes:
Called with IoCancelSpinLock held. Lock Irql is stored in Irp->CancelIrql. Releases IoCancelSpinLock before returning.
--*/
{ PIO_STACK_LOCATION irpSp; PCN_FSCONTEXT fsContext;
irpSp = IoGetCurrentIrpStackLocation(Irp); fsContext = (PCN_FSCONTEXT) irpSp->FileObject->FsContext;
IoSetCancelRoutine(Irp, NULL);
CnDereferenceFsContext(fsContext);
IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(( "[Clusnet] Completing irp %p fileobj %p, status %lx\n", Irp, irpSp->FileObject, Status )); }
if (Irp->Cancel || fsContext->CancelIrps) {
IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(("[Clusnet] Completed irp %p was cancelled\n", Irp)); }
Status = (NTSTATUS) STATUS_CANCELLED; BytesReturned = 0; }
CnReleaseCancelSpinLock(Irp->CancelIrql);
Irp->IoStatus.Status = (NTSTATUS) Status; Irp->IoStatus.Information = BytesReturned;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return;
} // CnCompletePendingRequest
PFILE_OBJECT CnBeginCancelRoutine( IN PIRP Irp )
/*++
Routine Description:
Performs common bookkeeping for irp cancellation.
Arguments:
Irp - Pointer to I/O request packet
Return Value:
A pointer to the file object on which the irp was submitted. This value must be passed to CnEndCancelRequest().
Notes:
Called with cancel spinlock held.
--*/
{ PIO_STACK_LOCATION irpSp; PCN_FSCONTEXT fsContext; NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT fileObject;
CnAssert(Irp->Cancel);
irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; fsContext = (PCN_FSCONTEXT) fileObject->FsContext;
IoSetCancelRoutine(Irp, NULL);
//
// Add a reference so the object can't be closed while the cancel routine
// is executing.
//
CnReferenceFsContext(fsContext);
IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(( "[Clusnet] Cancelling irp %p fileobj %p\n", Irp, fileObject )); }
return(fileObject);
} // CnBeginCancelRoutine
VOID CnEndCancelRoutine( PFILE_OBJECT FileObject ) /*++
Routine Description:
Performs common bookkeeping for irp cancellation.
Arguments:
Return Value:
Notes:
Called with cancel spinlock held.
--*/ {
PCN_FSCONTEXT fsContext = (PCN_FSCONTEXT) FileObject->FsContext;
//
// Remove the reference placed on the endpoint by the cancel routine.
//
CnDereferenceFsContext(fsContext);
IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(( "[Clusnet] Finished cancelling, fileobj %p\n", FileObject )); }
return;
} // CnEndCancelRoutine
NTSTATUS CnDispatchInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
This is the dispatch routine for Internal Device Control IRPs. This is the hot path for kernel-mode TDI clients.
Arguments:
DeviceObject - Pointer to device object for target device Irp - Pointer to I/O request packet
Return Value:
An NT status code.
--*/
{ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS; ULONG fileType = (ULONG)((ULONG_PTR)irpSp->FileObject->FsContext2); #if DBG
KIRQL entryIrql = KeGetCurrentIrql(); #endif // DBG
Irp->IoStatus.Information = 0;
if (DeviceObject == CdpDeviceObject) { if (fileType == TDI_TRANSPORT_ADDRESS_FILE) { if (irpSp->MinorFunction == TDI_SEND_DATAGRAM) { status = CxSendDatagram(Irp, irpSp);
#if DBG
CnAssert(entryIrql == KeGetCurrentIrql()); #endif // DBG
return(status); } else if (irpSp->MinorFunction == TDI_RECEIVE_DATAGRAM) { status = CxReceiveDatagram(Irp, irpSp); #if DBG
CnAssert(entryIrql == KeGetCurrentIrql()); #endif // DBG
return(status); } else if (irpSp->MinorFunction == TDI_SET_EVENT_HANDLER) { status = CxSetEventHandler(Irp, irpSp);
#if DBG
CnAssert(entryIrql == KeGetCurrentIrql()); #endif // DBG
return(status); }
//
// Fall through to common code.
//
}
//
// These functions are common to all endpoint types.
//
switch(irpSp->MinorFunction) {
case TDI_QUERY_INFORMATION: status = CxQueryInformation(Irp, irpSp); break;
case TDI_SET_INFORMATION: case TDI_ACTION: CNPRINT(( "[Clusnet] Call to unimplemented TDI function 0x%x\n", irpSp->MinorFunction )); status = STATUS_NOT_IMPLEMENTED; break;
default: CNPRINT(( "[Clusnet] Call to invalid TDI function 0x%x\n", irpSp->MinorFunction )); status = STATUS_INVALID_DEVICE_REQUEST; } } else { CNPRINT(( "[Clusnet] Invalid internal device control function 0x%x on device %ws\n", irpSp->MinorFunction, DD_CLUSNET_DEVICE_NAME ));
status = STATUS_INVALID_DEVICE_REQUEST; }
Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
#if DBG
CnAssert(entryIrql == KeGetCurrentIrql()); #endif // DBG
return(status);
} // CnDispatchInternalDeviceControl
NTSTATUS CnDispatchDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
This is the top-level dispatch routine for Device Control IRPs.
Arguments:
DeviceObject - Pointer to device object for target device Irp - Pointer to I/O request packet
Return Value:
An NT status code.
Notes:
This routine completes any IRPs for which the return code is not STATUS_PENDING.
--*/
{ NTSTATUS status; CCHAR ioIncrement = IO_NO_INCREMENT; BOOLEAN resourceAcquired = FALSE; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); ULONG ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode; ULONG fileType = (ULONG) ((ULONG_PTR) irpSp->FileObject->FsContext2);
PAGED_CODE();
//
// Set this in advance. Any subsequent dispatch routine that cares
// about it will modify it itself.
//
Irp->IoStatus.Information = 0;
//
// The following commands are valid on only TDI address objects.
//
if (ioControlCode == IOCTL_CX_IGNORE_NODE_STATE) { if (fileType == TDI_TRANSPORT_ADDRESS_FILE) { status = CxDispatchDeviceControl(Irp, irpSp); } else { //
// Not handled. Return an error.
//
status = STATUS_INVALID_DEVICE_REQUEST; } goto complete_request; }
//
// No other commands are valid on TDI address objects.
//
if (fileType == TDI_TRANSPORT_ADDRESS_FILE) { //
// Not handled. Return an error.
//
status = STATUS_INVALID_DEVICE_REQUEST; goto complete_request; } //
// The remaining commands are valid for control channels.
//
CnAssert(fileType == TDI_CONTROL_CHANNEL_FILE); //
// The following set of commands affect only this file object and
// can be issued at any time. We do not need to hold the CnResource
// in order to process them. Nor do we need to be in the initialized.
// state.
//
switch(ioControlCode) { case IOCTL_CLUSNET_SET_EVENT_MASK: { PCN_FSCONTEXT fsContext = irpSp->FileObject->FsContext; PCLUSNET_SET_EVENT_MASK_REQUEST request; ULONG requestSize; request = (PCLUSNET_SET_EVENT_MASK_REQUEST) Irp->AssociatedIrp.SystemBuffer; requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength; if (requestSize >= sizeof(CLUSNET_SET_EVENT_MASK_REQUEST)) { //
// Kernel mode callers must supply a callback.
// User mode callers must not.
//
if ( !( (Irp->RequestorMode == KernelMode) && (request->KmodeEventCallback == NULL) ) && !( (Irp->RequestorMode == UserMode) && (request->KmodeEventCallback != NULL) ) ) { status = CnSetEventMask( fsContext, request ); } else { status = STATUS_INVALID_PARAMETER; } } else { status = STATUS_INVALID_PARAMETER; } } goto complete_request; case IOCTL_CLUSNET_GET_NEXT_EVENT: { PCLUSNET_EVENT_RESPONSE response; ULONG responseSize; responseSize = irpSp->Parameters.DeviceIoControl.OutputBufferLength; if ( (responseSize < sizeof(CLUSNET_EVENT_RESPONSE))) { status = STATUS_INVALID_PARAMETER; } else { status = CnGetNextEvent( Irp, irpSp ); ioIncrement = IO_NETWORK_INCREMENT; } } goto complete_request; case IOCTL_CLUSNET_SET_IAMALIVE_PARAM: { PCLUSNET_SET_IAMALIVE_PARAM_REQUEST request; ULONG requestSize;
request = (PCLUSNET_SET_IAMALIVE_PARAM_REQUEST) Irp->AssociatedIrp.SystemBuffer; requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
if ((requestSize < sizeof(CLUSNET_SET_IAMALIVE_PARAM_REQUEST)) || (request->Action >= ClussvcHangActionMax)) { status = STATUS_INVALID_PARAMETER; } else {
PEPROCESS process = PsGetCurrentProcess(); IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(( "[ClusNet] Set Clussvc to Clusnet Hb params: Timeout=%u(s) Action=%u\n", request->Timeout, (ULONG)request->Action )); } CnTrace(HBEAT_EVENT, HbTraceSetIamaliveParam, "[ClusNet] Set Clussvc to Clusnet Hb params: Timeout=%u(s) Action=%u\n", request->Timeout, (ULONG)request->Action ); ClussvcClusnetHbTimeoutTicks = (request->Timeout * 1000)/HEART_BEAT_PERIOD; ClussvcClusnetHbTimeoutAction = request->Action; InterlockedExchange(&ClussvcClusnetHbTickCount, 0);
// Save the timeout in seconds for the bugcheck parameter.
ClussvcClusnetHbTimeoutSeconds = request->Timeout;
//
// Log a warning if the current process doesn't match the monitored
// process (clussvc should set its own params).
//
if (process != ClussvcProcessObject) { IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(( "[ClusNet] Warning: process configuring hang detection " "parameters (%p) is not clussvc (%p).\n", process, ClussvcProcessObject )); } CnTrace(HBEAT_ERROR, HbTraceSetIamaliveParamProcMismatch, "[ClusNet] Warning: process configuring hang detection " "parameters (%p) is not clussvc (%p).\n", process, ClussvcProcessObject ); }
status = STATUS_SUCCESS; } } goto complete_request;
case IOCTL_CLUSNET_IAMALIVE: { InterlockedExchange(&ClussvcClusnetHbTickCount, ClussvcClusnetHbTimeoutTicks); status = STATUS_SUCCESS; } goto complete_request; } // end of switch
//
// Not handled yet. Fall through.
//
if (ClusnetIsGeneralIoctl(ioControlCode)) {
if (!ClusnetIsNTEIoctl(ioControlCode)) {
//
// The following commands require exclusive access to CnResource.
//
resourceAcquired = CnAcquireResourceExclusive( CnResource, TRUE );
if (!resourceAcquired) { CnAssert(resourceAcquired == TRUE); status = STATUS_UNSUCCESSFUL; goto complete_request; }
switch(ioControlCode) {
case IOCTL_CLUSNET_INITIALIZE:
if (CnState == CnStateShutdown) { PCLUSNET_INITIALIZE_REQUEST request; ULONG requestSize;
request = (PCLUSNET_INITIALIZE_REQUEST) Irp->AssociatedIrp.SystemBuffer;
requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
if (requestSize < sizeof(CLUSNET_INITIALIZE_REQUEST)) { status = STATUS_INVALID_PARAMETER; } else { status = CnInitialize( request->LocalNodeId, request->MaxNodes ); } } else { status = STATUS_INVALID_DEVICE_REQUEST; }
goto complete_request;
case IOCTL_CLUSNET_ENABLE_SHUTDOWN_ON_CLOSE: status = CnEnableShutdownOnClose(Irp); goto complete_request;
case IOCTL_CLUSNET_DISABLE_SHUTDOWN_ON_CLOSE: { PCN_FSCONTEXT fsContext = irpSp->FileObject->FsContext;
fsContext->ShutdownOnClose = FALSE;
if ( ClussvcProcessHandle ) {
CnCloseProcessHandle( &ClussvcProcessHandle ); ClussvcProcessHandle = NULL; }
status = STATUS_SUCCESS; } goto complete_request;
case IOCTL_CLUSNET_HALT: status = CnShutdown();
CnReleaseResourceForThread( CnResource, (ERESOURCE_THREAD) PsGetCurrentThread() );
resourceAcquired = FALSE;
//
// Issue a Halt event. If clusdisk still has a handle
// to clusnet, then it will release its reservations.
//
CnIssueEvent( ClusnetEventHalt, 0, 0 );
goto complete_request;
case IOCTL_CLUSNET_SHUTDOWN: status = CnShutdown(); goto complete_request;
case IOCTL_CLUSNET_SET_MEMORY_LOGGING: { PCLUSNET_SET_MEM_LOGGING_REQUEST request; ULONG requestSize;
request = (PCLUSNET_SET_MEM_LOGGING_REQUEST) Irp->AssociatedIrp.SystemBuffer;
requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
if ( (requestSize < sizeof(CLUSNET_SET_MEM_LOGGING_REQUEST))) {
status = STATUS_INVALID_PARAMETER; } else {
status = CnSetMemLogging( request ); } } goto complete_request; #if DBG
case IOCTL_CLUSNET_SET_DEBUG_MASK: { PCLUSNET_SET_DEBUG_MASK_REQUEST request; ULONG requestSize;
request = (PCLUSNET_SET_DEBUG_MASK_REQUEST) Irp->AssociatedIrp.SystemBuffer;
requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
if (requestSize < sizeof(CLUSNET_SET_DEBUG_MASK_REQUEST)) { status = STATUS_INVALID_PARAMETER; } else { CnDebug = request->DebugMask; status = STATUS_SUCCESS; } } goto complete_request; #endif // DBG
} // end switch
} else {
//
// The following commands are only valid if we are
// in the initialized state. The resource is
// acquired to start the operation in the proper
// state; however, the dispatched routines are
// reentrant, so the resource can be released before
// the IRPs complete.
//
resourceAcquired = CnAcquireResourceShared( CnResource, TRUE );
if (!resourceAcquired) { CnAssert(resourceAcquired == TRUE); status = STATUS_UNSUCCESSFUL; goto complete_request; }
if (CnState != CnStateInitialized) { status = STATUS_DEVICE_NOT_READY; goto complete_request; }
switch(ioControlCode) {
case IOCTL_CLUSNET_ADD_NTE: status = IpaAddNTE(Irp, irpSp);
goto complete_request;
case IOCTL_CLUSNET_DELETE_NTE: status = IpaDeleteNTE(Irp, irpSp);
goto complete_request;
case IOCTL_CLUSNET_SET_NTE_ADDRESS: status = IpaSetNTEAddress(Irp, irpSp);
goto complete_request;
case IOCTL_CLUSNET_ADD_NBT_INTERFACE: { PNETBT_ADD_DEL_IF request; ULONG requestSize; PNETBT_ADD_DEL_IF response; ULONG responseSize;
request = (PNETBT_ADD_DEL_IF) Irp->AssociatedIrp.SystemBuffer;
response = (PNETBT_ADD_DEL_IF) request;
requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
responseSize = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
//
// Validate the request and response buffers.
//
if ( // request data structure
(requestSize < sizeof(NETBT_ADD_DEL_IF)) ||
// request device name must fit in request buffer
(requestSize < (FIELD_OFFSET(NETBT_ADD_DEL_IF, IfName[0]) + request->Length)) ||
// request device name must be null-terminated
// StringCchLengthW will probe for NULL termination
// up to the character count in the second
// parameter. If there is no NULL termination
// within that count, an error is returned.
(StringCchLengthW( (LPCWSTR)&(request->IfName[0]), request->Length / sizeof(WCHAR), NULL ) != S_OK) ||
// response data structure
(responseSize < sizeof(NETBT_ADD_DEL_IF)) ) { status = STATUS_INVALID_PARAMETER; } else { status = NbtAddIf( request, requestSize, response, &responseSize );
CnAssert(status != STATUS_PENDING);
if (NT_SUCCESS(status)) { Irp->IoStatus.Information = responseSize; } } } goto complete_request;
case IOCTL_CLUSNET_DEL_NBT_INTERFACE: { PNETBT_ADD_DEL_IF request; ULONG requestSize;
request = (PNETBT_ADD_DEL_IF) Irp->AssociatedIrp.SystemBuffer;
requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
//
// Validate the request buffer. There is no response
// buffer.
//
if ( // request data structure
(requestSize < sizeof(NETBT_ADD_DEL_IF)) ||
// request device name must fit in request buffer
(requestSize < (FIELD_OFFSET(NETBT_ADD_DEL_IF, IfName[0]) + request->Length)) ||
// request device name must be null-terminated
// StringCchLengthW will probe for NULL termination
// up to the character count in the second
// parameter. If there is no NULL termination
// within that count, an error is returned.
(StringCchLengthW( (LPCWSTR)&(request->IfName[0]), request->Length / sizeof(WCHAR), NULL ) != S_OK) ) { status = STATUS_INVALID_PARAMETER; } else { status = NbtDeleteIf(request, requestSize);
CnAssert(status != STATUS_PENDING); } } goto complete_request;
} // end switch
} //
// Not handled. Return an error.
//
status = STATUS_INVALID_DEVICE_REQUEST; goto complete_request; } else { //
// The following commands require shared access to CnResource.
// They are only valid in the initialized state.
//
resourceAcquired = CnAcquireResourceShared(CnResource, TRUE); if (!resourceAcquired) { CnAssert(resourceAcquired == TRUE); status = STATUS_UNSUCCESSFUL; goto complete_request; } if (CnState == CnStateInitialized) { if (ClusnetIsTransportIoctl(ioControlCode)) { status = CxDispatchDeviceControl(Irp, irpSp); } else { //
// Not handled. Return an error.
//
status = STATUS_INVALID_DEVICE_REQUEST; } } else { //
// We haven't been initialized yet. Return an error.
//
status = STATUS_DEVICE_NOT_READY; } } complete_request:
if (resourceAcquired) { CnReleaseResourceForThread( CnResource, (ERESOURCE_THREAD) PsGetCurrentThread() ); }
if (status != STATUS_PENDING) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, ioIncrement); }
return(status);
} // CnDispatchDeviceControl
NTSTATUS CnDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
This is the generic dispatch routine for the driver.
Arguments:
DeviceObject - Pointer to device object for target device Irp - Pointer to I/O request packet
Return Value:
An NT status code.
--*/
{ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS; #if DBG
KIRQL entryIrql = KeGetCurrentIrql(); #endif // DBG
PAGED_CODE();
CnAssert(irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL);
switch (irpSp->MajorFunction) {
case IRP_MJ_CREATE: status = CnCreate(DeviceObject, Irp, irpSp); break;
case IRP_MJ_CLEANUP: status = CnCleanup(DeviceObject, Irp, irpSp); break;
case IRP_MJ_CLOSE: status = CnClose(DeviceObject, Irp, irpSp); break;
case IRP_MJ_SHUTDOWN: IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(("[ClusNet] Processing shutdown notification...\n")); }
{ BOOLEAN acquired = CnAcquireResourceExclusive( CnResource, TRUE );
CnAssert(acquired == TRUE);
(VOID) CnShutdown();
if (acquired) { CnReleaseResourceForThread( CnResource, (ERESOURCE_THREAD) PsGetCurrentThread() ); }
//
// Issue a Halt event. If clusdisk still has a handle
// to clusnet, then it will release its reservations
//
CnIssueEvent( ClusnetEventHalt, 0, 0 );
status = STATUS_SUCCESS; }
IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(("[ClusNet] Shutdown processing complete.\n")); }
break;
default: IF_CNDBG(CN_DEBUG_IRP) { CNPRINT(( "[ClusNet] Received IRP with unsupported " "major function 0x%lx\n", irpSp->MajorFunction )); } status = STATUS_INVALID_DEVICE_REQUEST; break; }
CnAssert(status != STATUS_PENDING);
Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
#if DBG
CnAssert(entryIrql == KeGetCurrentIrql()); #endif // DBG
return(status);
} // CnDispatch
NTSTATUS CnCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp )
/*++
Routine Description:
Handler for create IRPs.
Arguments:
DeviceObject - Pointer to the device object for this request. Irp - Pointer to I/O request packet
Return Value:
An NT status code.
Notes:
This routine never returns STATUS_PENDING. The calling routine must complete the IRP.
--*/
{ PCN_FSCONTEXT fsContext; FILE_FULL_EA_INFORMATION *ea; FILE_FULL_EA_INFORMATION UNALIGNED *targetEA; NTSTATUS status;
PAGED_CODE();
//
// Reject unathorized opens
//
if ( (IrpSp->FileObject->RelatedFileObject != NULL) || (IrpSp->FileObject->FileName.Length != 0) ) { return(STATUS_ACCESS_DENIED); }
ea = (PFILE_FULL_EA_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
if ((DeviceObject == CdpDeviceObject) && (ea != NULL)) {
IF_CNDBG(CN_DEBUG_OPEN) { CNPRINT(( "[ClusNet] Opening address object, file object %p\n", IrpSp->FileObject )); }
//
// Verify that the caller has access to open CDP sockets.
//
if (!CnPerformSecurityCheck(Irp, IrpSp, &status)) { IF_CNDBG(CN_DEBUG_OPEN) { CNPRINT(( "[ClusNet] Failed CDP security check, error %lx\n", status )); } return(STATUS_ACCESS_DENIED); }
//
// This is the CDP device. This should be an address object open.
//
targetEA = CnFindEA( ea, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH );
if (targetEA != NULL) { IrpSp->FileObject->FsContext2 = (PVOID) TDI_TRANSPORT_ADDRESS_FILE;
//
// Open an address object. This will also allocate the common
// file object context structure.
//
status = CxOpenAddress( &fsContext, (TRANSPORT_ADDRESS UNALIGNED *) &(targetEA->EaName[targetEA->EaNameLength + 1]), targetEA->EaValueLength ); } else { IF_CNDBG(CN_DEBUG_OPEN) { CNPRINT(( "[ClusNet] No transport address in EA!\n" )); } status = STATUS_INVALID_PARAMETER; } } else { //
// This is a control channel open.
//
IF_CNDBG(CN_DEBUG_OPEN) { IF_CNDBG(CN_DEBUG_OPEN) { CNPRINT(( "[ClusNet] Opening control channel, file object %p\n", IrpSp->FileObject )); } }
//
// Allocate our common file object context structure.
//
fsContext = CnAllocatePool(sizeof(CN_FSCONTEXT));
if (fsContext != NULL) { IrpSp->FileObject->FsContext2 = (PVOID) TDI_CONTROL_CHANNEL_FILE; CN_INIT_SIGNATURE(fsContext, CN_CONTROL_CHANNEL_SIG);
//
// Check the sharing flags. If this is an exclusive open, check
// to make sure there isn't already an exclusive open outstanding.
//
if (IrpSp->Parameters.Create.ShareAccess == 0) { BOOLEAN acquired = CnAcquireResourceExclusive( CnResource, TRUE );
CnAssert(acquired == TRUE);
if (CnExclusiveChannel == NULL) { CnExclusiveChannel = fsContext; //
// (Re)enable the halt processing mechanism.
//
CnEnableHaltProcessing();
status = STATUS_SUCCESS; } else { CnFreePool(fsContext); status = STATUS_SHARING_VIOLATION; }
if (acquired) { CnReleaseResourceForThread( CnResource, (ERESOURCE_THREAD) PsGetCurrentThread() ); } } else { status = STATUS_SUCCESS; } } else { status = STATUS_INSUFFICIENT_RESOURCES; } }
if (status == STATUS_SUCCESS) { IrpSp->FileObject->FsContext = fsContext;
fsContext->FileObject = IrpSp->FileObject; fsContext->ReferenceCount = 1; fsContext->CancelIrps = FALSE; fsContext->ShutdownOnClose = FALSE;
KeInitializeEvent( &(fsContext->CleanupEvent), SynchronizationEvent, FALSE );
//
// init the Event list stuff. We use the empty list test on the
// Linkage field to see if this context block is already been linked
// to the event file object list
//
fsContext->EventMask = 0; InitializeListHead( &fsContext->EventList ); InitializeListHead( &fsContext->Linkage ); fsContext->EventIrp = NULL; }
return(status);
} // CnCreate
NTSTATUS CnCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp )
/*++
Routine Description:
Cancels all outstanding Irps on a device object and waits for them to be completed before returning.
Arguments:
DeviceObject - Pointer to the device object on which the Irp was received. Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp.
Return Value:
An NT status code.
Notes:
This routine may block. This routine never returns STATUS_PENDING. The calling routine must complete the IRP.
--*/
{ CN_IRQL oldIrql; NTSTATUS status; ULONG fileType = (ULONG)((ULONG_PTR)IrpSp->FileObject->FsContext2); PCN_FSCONTEXT fsContext = (PCN_FSCONTEXT) IrpSp->FileObject->FsContext; PLIST_ENTRY EventEntry; PIRP eventIrp;
if (fileType == TDI_TRANSPORT_ADDRESS_FILE) { //
// This is an address object.
//
CnAssert(DeviceObject == CdpDeviceObject);
status = CxCloseAddress(fsContext); } else { //
// This is a control channel.
//
CnAssert(fileType == TDI_CONTROL_CHANNEL_FILE);
//
// If this channel has a shutdown trigger enabled,
// shutdown the driver.
//
if (fsContext->ShutdownOnClose) {
BOOLEAN shutdownScheduled;
//
// Bug 303422: CnShutdown closes handles that were opened
// in the context of the system process. However, attaching
// to the system process during shutdown can cause a
// bugcheck under certain conditions. The only alternative
// is to defer executing of CnShutdown to a system worker
// thread.
//
// Rather than creating a new event object, leverage the
// cleanup event in the fscontext. It is reset before use
// below.
//
KeClearEvent(&(fsContext->CleanupEvent));
shutdownScheduled = CnHaltOperation(&(fsContext->CleanupEvent));
if (shutdownScheduled) { status = KeWaitForSingleObject( &(fsContext->CleanupEvent), (Irp->RequestorMode == KernelMode ? Executive : UserRequest), KernelMode, FALSE, NULL );
CnAssert(NT_SUCCESS(status));
status = STATUS_SUCCESS; }
//
// issue a Halt event. If clusdisk still has a handle
// to clusnet, then it will release its reservations
//
CnIssueEvent( ClusnetEventHalt, 0, 0 ); }
//
// if this guy forgot to clear the event mask before
// closing the handle, do the appropriate cleanup
// now.
//
if ( fsContext->EventMask ) { CLUSNET_SET_EVENT_MASK_REQUEST EventRequest;
EventRequest.EventMask = 0;
//
// cannot proceed if CnSetEventMask returns a timeout
// error. this indicates that the fsContext has not
// been removed from the EventFileHandles list because
// of lock starvation.
do { status = CnSetEventMask( fsContext, &EventRequest ); } while ( status == STATUS_TIMEOUT ); CnAssert( status == STATUS_SUCCESS ); }
CnAcquireCancelSpinLock( &oldIrql ); CnAcquireLockAtDpc( &EventLock );
if ( fsContext->EventIrp != NULL ) {
eventIrp = fsContext->EventIrp; fsContext->EventIrp = NULL;
CnReleaseLockFromDpc( &EventLock ); eventIrp->CancelIrql = oldIrql; CnCompletePendingRequest(eventIrp, STATUS_CANCELLED, 0); } else { CnReleaseLockFromDpc( &EventLock ); CnReleaseCancelSpinLock( oldIrql ); } }
//
// Remove the initial reference and wait for all pending work
// to complete.
//
fsContext->CancelIrps = TRUE; KeResetEvent(&(fsContext->CleanupEvent));
CnDereferenceFsContext(fsContext);
IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(( "[ClusNet] Waiting for completion of Irps on file object %p\n", IrpSp->FileObject )); }
status = KeWaitForSingleObject( &(fsContext->CleanupEvent), (Irp->RequestorMode == KernelMode ? Executive : UserRequest), KernelMode, FALSE, NULL );
CnAssert(NT_SUCCESS(status));
status = STATUS_SUCCESS;
IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(( "[Clusnet] Wait on file object %p finished\n", IrpSp->FileObject )); }
return(status);
} // CnCleanup
NTSTATUS CnClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp )
/*++
Routine Description:
Dispatch routine for MJ_CLOSE IRPs. Performs final cleanup of the open file object.
Arguments:
DeviceObject - Pointer to the device object on which the Irp was received. Irp - Pointer to I/O request packet IrpSp - Pointer to the current stack location in the Irp.
Return Value:
An NT status code.
Notes:
This routine never returns STATUS_PENDING. The calling routine must complete the IRP.
--*/
{ BOOLEAN acquired; PCN_FSCONTEXT fsContext = (PCN_FSCONTEXT) IrpSp->FileObject->FsContext; ULONG fileType = (ULONG)((ULONG_PTR)IrpSp->FileObject->FsContext2);
CnAssert(fsContext->ReferenceCount == 0); CnAssert(IsListEmpty(&(fsContext->EventList)));
if (fileType == TDI_CONTROL_CHANNEL_FILE) { acquired = CnAcquireResourceExclusive( CnResource, TRUE );
CnAssert(acquired == TRUE);
if (CnExclusiveChannel == fsContext) { CnExclusiveChannel = NULL; }
if (acquired) { CnReleaseResourceForThread( CnResource, (ERESOURCE_THREAD) PsGetCurrentThread() ); } }
IF_CNDBG(CN_DEBUG_CLOSE) { CNPRINT(( "[ClusNet] Close on file object %p\n", IrpSp->FileObject )); }
CnFreePool(fsContext);
return(STATUS_SUCCESS);
} // CnClose
FILE_FULL_EA_INFORMATION UNALIGNED * CnFindEA( PFILE_FULL_EA_INFORMATION StartEA, CHAR *TargetName, USHORT TargetNameLength ) /*++
Routine Description:
Parses and extended attribute list for a given target attribute.
Arguments:
StartEA - the first extended attribute in the list. TargetName - the name of the target attribute. TargetNameLength - the length of the name of the target attribute.
Return Value:
A pointer to the requested attribute or NULL if the target wasn't found.
--*/ { USHORT i; BOOLEAN found; FILE_FULL_EA_INFORMATION UNALIGNED * CurrentEA; FILE_FULL_EA_INFORMATION UNALIGNED * NextEA;
PAGED_CODE();
NextEA = (FILE_FULL_EA_INFORMATION UNALIGNED *) StartEA;
do { found = TRUE;
CurrentEA = NextEA; NextEA = (FILE_FULL_EA_INFORMATION UNALIGNED *) ( ((PUCHAR) StartEA) + CurrentEA->NextEntryOffset);
if (CurrentEA->EaNameLength != TargetNameLength) { continue; }
for (i=0; i < CurrentEA->EaNameLength; i++) { if (CurrentEA->EaName[i] == TargetName[i]) { continue; } found = FALSE; break; }
if (found) { return(CurrentEA); }
} while(CurrentEA->NextEntryOffset != 0);
return(NULL);
} // CnFindEA
NTSTATUS CnEnableShutdownOnClose( PIRP Irp ) { NTSTATUS status; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); PCN_FSCONTEXT fsContext = irpSp->FileObject->FsContext; ULONG requestSize; CLIENT_ID ClientId; OBJECT_ATTRIBUTES ObjAttributes; PCLUSNET_SHUTDOWN_ON_CLOSE_REQUEST request;
PAGED_CODE(); request = (PCLUSNET_SHUTDOWN_ON_CLOSE_REQUEST) Irp->AssociatedIrp.SystemBuffer;
requestSize = irpSp->Parameters.DeviceIoControl.InputBufferLength;
if ( requestSize >= sizeof(CLUSNET_SHUTDOWN_ON_CLOSE_REQUEST) ) { //
// illegal for kernel mode
//
if ( Irp->RequestorMode != KernelMode ) { //
// Get a handle to the cluster service process.
// This is used to kill the service if a poison
// pkt is received. Since a kernel worker thread
// is used to kill the cluster service, we need
// to acquire the handle in the system process.
//
IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(("[Clusnet] Acquiring process handle\n")); }
if (ClussvcProcessHandle == NULL) { KeAttachProcess( CnSystemProcess );
ClientId.UniqueThread = (HANDLE)NULL; ClientId.UniqueProcess = UlongToHandle(request->ProcessId);
InitializeObjectAttributes( &ObjAttributes, NULL, 0, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL );
status = ZwOpenProcess( &ClussvcProcessHandle, 0, &ObjAttributes, &ClientId );
if ( NT_SUCCESS( status )) {
NTSTATUS subStatus;
fsContext->ShutdownOnClose = TRUE; //
// Get the clussvc process pointer. This is
// used only as an informational parameter
// in the hang detection bugcheck, so a
// failure is non-fatal.
//
subStatus = ObReferenceObjectByHandle( ClussvcProcessHandle, 0, NULL, KernelMode, &ClussvcProcessObject, NULL ); if (NT_SUCCESS(subStatus)) { //
// Immediately drop the reference so that we
// don't need to drop it later. Since the
// process object is only used for informational
// purposes, we are not concerned with its
// reference count. We save the pointer and
// assume it is good as long as we hold a
// handle to the process.
//
ObDereferenceObject(ClussvcProcessObject); } else { IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(( "[Clusnet] Failed to reference " "clussvc process object by handle, " "status %08X.\n", subStatus )); } CnTrace(HBEAT_ERROR, HbTraceClussvcProcessObj, "[Clusnet] Failed to reference " "clussvc process object by handle, " "status %!status!.\n", subStatus ); ClussvcProcessObject = NULL; } } else { IF_CNDBG(CN_DEBUG_INIT) { CNPRINT(( "[Clusnet] ZwOpenProcess failed. status = %08X\n", status )); } }
KeDetachProcess();
} else { status = STATUS_INVALID_DEVICE_REQUEST; } } else { status = STATUS_INVALID_PARAMETER_MIX; } } else { status = STATUS_INVALID_PARAMETER; }
return(status);
} // CnEnableShutdownOnClose
BOOLEAN CnPerformSecurityCheck( PIRP Irp, PIO_STACK_LOCATION IrpSp, PNTSTATUS Status ) /*++
Routine Description:
Compares security context of the endpoint creator to that of the administrator and local system.
Arguments:
Irp - Pointer to I/O request packet.
IrpSp - pointer to the IO stack location to use for this request.
Status - returns status generated by access check on failure.
Return Value:
TRUE - the socket creator has admin or local system privilige FALSE - the socket creator is just a plain user
Notes:
This code was lifted from AFD.
--*/
{ BOOLEAN accessGranted; PACCESS_STATE accessState; PIO_SECURITY_CONTEXT securityContext; PPRIVILEGE_SET privileges = NULL; ACCESS_MASK grantedAccess; PGENERIC_MAPPING genericMapping; ACCESS_MASK accessMask = GENERIC_ALL;
//
// Enable access to all the globally defined SIDs
//
genericMapping = IoGetFileObjectGenericMapping();
RtlMapGenericMask(&accessMask, genericMapping);
securityContext = IrpSp->Parameters.Create.SecurityContext; accessState = securityContext->AccessState;
SeLockSubjectContext(&accessState->SubjectSecurityContext);
accessGranted = SeAccessCheck( CdpAdminSecurityDescriptor, &accessState->SubjectSecurityContext, TRUE, accessMask, 0, &privileges, IoGetFileObjectGenericMapping(), (KPROCESSOR_MODE)((IrpSp->Flags & SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode), &grantedAccess, Status );
if (privileges) { (VOID) SeAppendPrivileges( accessState, privileges ); SeFreePrivileges(privileges); }
if (accessGranted) { accessState->PreviouslyGrantedAccess |= grantedAccess; accessState->RemainingDesiredAccess &= ~( grantedAccess | MAXIMUM_ALLOWED ); CnAssert(NT_SUCCESS(*Status)); } else { CnAssert(!NT_SUCCESS(*Status)); } SeUnlockSubjectContext(&accessState->SubjectSecurityContext);
return (accessGranted); } // CnPerformSecurityCheck
|