|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
miscsvc.c
Abstract:
This module contains miscellaneous SPUD services.
Author:
Keith Moore (keithmo) 09-Feb-1998
Revision History:
--*/
#include "spudp.h"
//
// Private prototypes.
//
NTSTATUS SpudpOpenSelfHandle( OUT PHANDLE SelfHandle );
NTSTATUS SpudpCloseSelfHandle( IN HANDLE SelfHandle );
NTSTATUS SpudpInitCompletionPort( IN HANDLE CompletionPort );
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SPUDInitialize )
#pragma alloc_text( PAGE, SPUDTerminate )
#pragma alloc_text( PAGE, SPUDCancel )
#pragma alloc_text( PAGE, SPUDGetCounts )
#pragma alloc_text( PAGE, SpudpOpenSelfHandle )
#pragma alloc_text( PAGE, SpudpCloseSelfHandle )
#endif
#if 0
NOT PAGEABLE -- SpudpInitCompletionPort #endif
//
// Public functions.
//
NTSTATUS SPUDInitialize( ULONG Version, HANDLE hPort )
/*++
Routine Description:
Performs per-process SPUD initialization.
Arguments:
Version - The SPUD interface version the user-mode client is expecting.
hPort - The handle to the user-mode code's I/O completion port.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status; HANDLE selfHandle;
//
// Sanity check.
//
PAGED_CODE();
status = SPUD_ENTER_SERVICE( "SPUDInitialize", FALSE );
if( !NT_SUCCESS(status) ) { return status; }
//
// Setup locals so we know how to cleanup on exit.
//
selfHandle = NULL;
//
// SPUD doesn't support kernel-mode callers. In fact, we don't
// even build the "system stubs" necessary to invoke SPUD from
// kernel-mode.
//
ASSERT( ExGetPreviousMode() == UserMode );
//
// Ensure we got the SPUD interface version number we're expecting.
//
if( Version != SPUD_VERSION ) { status = STATUS_INVALID_PARAMETER; }
//
// Open an exclusive handle to ourselves.
//
if( NT_SUCCESS(status) ) { status = SpudpOpenSelfHandle( &selfHandle ); }
//
// Reference the I/O completion port handle so we can use the
// pointer directly in KeInsertQueue().
//
if( NT_SUCCESS(status) ) { status = SpudpInitCompletionPort( hPort ); }
//
// Remember that we're initialized.
//
if( NT_SUCCESS(status) ) { SpudSelfHandle = selfHandle; } else {
//
// Fatal error, cleanup our self handle if we managed to
// open it.
//
if( selfHandle != NULL ) { SpudpCloseSelfHandle( selfHandle ); }
}
SPUD_LEAVE_SERVICE( "SPUDInitialize", status, FALSE ); return status;
} // SPUDInitialize
NTSTATUS SPUDTerminate( VOID )
/*++
Routine Description:
Performs per-process SPUD termination.
Arguments:
None.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status;
//
// Sanity check.
//
PAGED_CODE();
status = SPUD_ENTER_SERVICE( "SPUDTerminate", TRUE );
if( !NT_SUCCESS(status) ) { return status; }
//
// SPUD doesn't support kernel-mode callers. In fact, we don't
// even build the "system stubs" necessary to invoke SPUD from
// kernel-mode.
//
ASSERT( ExGetPreviousMode() == UserMode );
//
// Close the handle we opened to ourself. The IRP_MJ_CLOSE handler
// will dereference the completion port and reset the global variables.
//
ASSERT( SpudSelfHandle != NULL ); SpudpCloseSelfHandle( SpudSelfHandle ); SpudSelfHandle = NULL;
ASSERT( status == STATUS_SUCCESS ); SPUD_LEAVE_SERVICE( "SPUDTerminate", status, FALSE ); return status;
} // SPUDTerminate
NTSTATUS SPUDCancel( IN PSPUD_REQ_CONTEXT reqContext )
/*++
Routine Description:
Cancels an outstanding XxxAndRecv() request.
Arguments:
reqContext - The user-mode context associated with the I/O request.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status; PSPUD_AFD_REQ_CONTEXT SpudReqContext; PIRP irp;
//
// Sanity check.
//
PAGED_CODE();
status = SPUD_ENTER_SERVICE( "SPUDCancel", TRUE );
if( !NT_SUCCESS(status) ) { return status; }
//
// SPUD doesn't support kernel-mode callers. In fact, we don't
// even build the "system stubs" necessary to invoke SPUD from
// kernel-mode.
//
ASSERT( ExGetPreviousMode() == UserMode );
try {
//
// Make sure we can write to reqContext
//
ProbeForRead( reqContext, sizeof(SPUD_REQ_CONTEXT), sizeof(ULONG) );
} except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); }
//
// Get the kernel-mode context from the user-mode context.
//
if( NT_SUCCESS(status) ) { SpudReqContext = SpudGetRequestContext( reqContext );
if( SpudReqContext == NULL ) { status = STATUS_INVALID_PARAMETER; } }
//
// Snag the IRP from the context & cancel the request.
//
if( NT_SUCCESS(status) ) { irp = SpudReqContext->Irp; if (irp) { IoCancelIrp(irp); } }
SPUD_LEAVE_SERVICE( "SPUDCancel", status, FALSE ); return status;
} // SPUDCancel
NTSTATUS SPUDGetCounts( PSPUD_COUNTERS UserCounters )
/*++
Routine Description:
Retrieves the SPUD activity counters.
Arguments:
UserCounters - Receives the counters.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status;
//
// Sanity check.
//
PAGED_CODE();
//
// N.B. We do not perform the usual driver initialization and
// exclusivity checks here. We *want* SPUDGetCounts to be callable
// by processes other than the server.
//
//
// SPUD doesn't support kernel-mode callers. In fact, we don't
// even build the "system stubs" necessary to invoke SPUD from
// kernel-mode.
//
ASSERT( ExGetPreviousMode() == UserMode );
try {
//
// The SpudCounters parameter must be writable.
//
ProbeForWrite( UserCounters, sizeof(*UserCounters), sizeof(ULONG) );
//
// Copy the counters to the user-mode buffer.
//
RtlCopyMemory( UserCounters, &SpudCounters, sizeof(SpudCounters) );
status = STATUS_SUCCESS;
} except( EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode(); }
return status;
} // SPUDGetCounts
//
// Private prototypes.
//
NTSTATUS SpudpOpenSelfHandle( OUT PHANDLE SelfHandle )
/*++
Routine Description:
Opens a handle to \Device\Spud and marks it so that it cannot be closed.
Arguments:
SelfHandle - Pointer to a variable that receives the handle.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status; HANDLE selfHandle; UNICODE_STRING deviceName; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; OBJECT_HANDLE_FLAG_INFORMATION handleInfo;
//
// Sanity check.
//
PAGED_CODE();
//
// Open an exclusive handle to ourselves.
//
RtlInitUnicodeString( &deviceName, SPUD_DEVICE_NAME );
InitializeObjectAttributes( &objectAttributes, // ObjectAttributes
&deviceName, // ObjectName
OBJ_CASE_INSENSITIVE, // Attributes
NULL, // RootDirectory
NULL // SecurityDescriptor
);
status = IoCreateFile( &selfHandle, // FileHandle
GENERIC_READ // DesiredAccess
| SYNCHRONIZE //
| FILE_READ_ATTRIBUTES, //
&objectAttributes, // ObjectAttributes
&ioStatusBlock, // IoStatusBlock
NULL, // AllocationSize
0L, // FileAttributes
0L, // ShareAccess
FILE_OPEN, // Disposition
0L, // CreateOptions
NULL, // EaBuffer
0, // EaLength
CreateFileTypeNone, // CreateFileType
NULL, // ExtraCreateParameters
IO_NO_PARAMETER_CHECKING // Options
);
if( !NT_SUCCESS(status) ) { return status; }
//
// Mark the handle so that those pesky user-mode applications
// can't close it. While we're at it, make the handle not
// inheritable.
//
handleInfo.ProtectFromClose = TRUE; handleInfo.Inherit = FALSE;
status = ZwSetInformationObject( selfHandle, // Handle
ObjectHandleFlagInformation, // ObjectInformationClass
&handleInfo, // ObjectInformation
sizeof(handleInfo) // ObjectInformationLength
);
//
// If all went well, then return the handle to the caller. Otherwise,
// carefully close the handle (avoiding an exception if the user-mode
// code has already closed it).
//
if( NT_SUCCESS(status) ) { *SelfHandle = selfHandle; } else { if( selfHandle != NULL ) { try { NtClose( selfHandle ); } except( EXCEPTION_EXECUTE_HANDLER ) { NOTHING; } } }
return status;
} // SpudpOpenSelfHandle
NTSTATUS SpudpCloseSelfHandle( IN HANDLE SelfHandle )
/*++
Routine Description:
Closes the handle opened by SpudpOpenSelfHandle().
Arguments:
SelfHandle - The handle to close.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status; OBJECT_HANDLE_FLAG_INFORMATION handleInfo;
//
// Sanity check.
//
PAGED_CODE();
//
// Mark the handle so that we can close it.
//
handleInfo.ProtectFromClose = FALSE; handleInfo.Inherit = FALSE;
status = ZwSetInformationObject( SelfHandle, // Handle
ObjectHandleFlagInformation, // ObjectInformationClass
&handleInfo, // ObjectInformation
sizeof(handleInfo) // ObjectInformationLength
);
//
// Carefully close the handle, even if the above APIs failed.
//
try { status = NtClose( SelfHandle ); } except( EXCEPTION_EXECUTE_HANDLER ) { status = GetExceptionCode(); }
return status;
} // SpudpCloseSelfHandle
NTSTATUS SpudpInitCompletionPort( IN HANDLE CompletionPort )
/*++
Routine Description:
References the specified completion port and sets our local reference count.
N.B. This is a separate routine so that SPUDInitialize() can be pageable.
Arguments:
CompletionPort - The completion port handle.
Return Value:
NTSTATUS - Completion status.
--*/
{
NTSTATUS status; KIRQL oldIrql; PVOID completionPort;
//
// Reference the I/O completion port handle so we can use the
// pointer directly in KeInsertQueue().
//
status = ObReferenceObjectByHandle( CompletionPort, // Handle
IO_COMPLETION_MODIFY_STATE, // DesiredAccess
NULL, // ObjectType
UserMode, // AccessMode
&completionPort, // Object
NULL // HandleInformation
);
//
// Remember that we're initialized.
//
if( NT_SUCCESS(status) ) { TRACE_OB_REFERENCE( completionPort );
KeAcquireSpinLock( &SpudCompletionPortLock, &oldIrql );
ASSERT( SpudCompletionPort == NULL ); ASSERT( SpudCompletionPortRefCount == 0 );
SpudCompletionPort = completionPort; SpudCompletionPortRefCount = 1;
KeReleaseSpinLock( &SpudCompletionPortLock, oldIrql ); }
return status;
} // SpudpInitCompletionPort
|