649 lines
12 KiB
C
649 lines
12 KiB
C
/*++
|
||
|
||
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
|
||
|