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
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
|
|
|
|
|