Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1928 lines
53 KiB

/*++
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