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