mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 14:03:32 +08:00
689 lines
20 KiB
C
689 lines
20 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/ex/callback.c
|
|
* PURPOSE: Executive callbacks
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* TYPES *********************************************************************/
|
|
|
|
/* Mapping for Callback Object */
|
|
GENERIC_MAPPING ExpCallbackMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ,
|
|
STANDARD_RIGHTS_WRITE | CALLBACK_MODIFY_STATE,
|
|
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
|
|
CALLBACK_ALL_ACCESS
|
|
};
|
|
|
|
/* Kernel Default Callbacks */
|
|
PCALLBACK_OBJECT SetSystemTimeCallback;
|
|
PCALLBACK_OBJECT SetSystemStateCallback;
|
|
PCALLBACK_OBJECT PowerStateCallback;
|
|
SYSTEM_CALLBACKS ExpInitializeCallback[] =
|
|
{
|
|
{&SetSystemTimeCallback, L"\\Callback\\SetSystemTime"},
|
|
{&SetSystemStateCallback, L"\\Callback\\SetSystemState"},
|
|
{&PowerStateCallback, L"\\Callback\\PowerState"},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
POBJECT_TYPE ExCallbackObjectType;
|
|
KEVENT ExpCallbackEvent;
|
|
EX_PUSH_LOCK ExpCallBackFlush;
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
VOID
|
|
NTAPI
|
|
ExInitializeCallBack(IN OUT PEX_CALLBACK Callback)
|
|
{
|
|
/* Initialize the fast reference */
|
|
ExInitializeFastReference(&Callback->RoutineBlock, NULL);
|
|
}
|
|
|
|
PEX_CALLBACK_ROUTINE_BLOCK
|
|
NTAPI
|
|
ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function,
|
|
IN PVOID Context)
|
|
{
|
|
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
|
|
|
|
/* Allocate a callback */
|
|
CallbackBlock = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(EX_CALLBACK_ROUTINE_BLOCK),
|
|
TAG_CALLBACK_ROUTINE_BLOCK);
|
|
if (CallbackBlock)
|
|
{
|
|
/* Initialize it */
|
|
CallbackBlock->Function = Function;
|
|
CallbackBlock->Context = Context;
|
|
ExInitializeRundownProtection(&CallbackBlock->RundownProtect);
|
|
}
|
|
|
|
/* Return it */
|
|
return CallbackBlock;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
|
|
{
|
|
/* Just free it from memory */
|
|
ExFreePoolWithTag(CallbackBlock, TAG_CALLBACK_ROUTINE_BLOCK);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
|
|
{
|
|
/* Wait on the rundown */
|
|
ExWaitForRundownProtectionRelease(&CallbackBlock->RundownProtect);
|
|
}
|
|
|
|
PEX_CALLBACK_FUNCTION
|
|
NTAPI
|
|
ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
|
|
{
|
|
/* Return the function */
|
|
return CallbackBlock->Function;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
|
|
{
|
|
/* Return the context */
|
|
return CallbackBlock->Context;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack,
|
|
IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock)
|
|
{
|
|
/* Release a fast reference */
|
|
if (!ExReleaseFastReference(&CallBack->RoutineBlock, CallbackBlock))
|
|
{
|
|
/* Take slow path */
|
|
ExReleaseRundownProtection(&CallbackBlock->RundownProtect);
|
|
}
|
|
}
|
|
|
|
PEX_CALLBACK_ROUTINE_BLOCK
|
|
NTAPI
|
|
ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack)
|
|
{
|
|
EX_FAST_REF OldValue;
|
|
ULONG_PTR Count;
|
|
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
|
|
|
|
/* Acquire a reference */
|
|
OldValue = ExAcquireFastReference(&CallBack->RoutineBlock);
|
|
Count = ExGetCountFastReference(OldValue);
|
|
|
|
/* Fail if there isn't any object */
|
|
if (!ExGetObjectFastReference(OldValue)) return NULL;
|
|
|
|
/* Check if we don't have a reference */
|
|
if (!Count)
|
|
{
|
|
/* FIXME: Race */
|
|
DPRINT1("Unhandled callback race condition\n");
|
|
ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* Get the callback block */
|
|
CallbackBlock = ExGetObjectFastReference(OldValue);
|
|
|
|
/* Check if this is the last reference */
|
|
if (Count == 1)
|
|
{
|
|
/* Acquire rundown protection */
|
|
if (ExfAcquireRundownProtectionEx(&CallbackBlock->RundownProtect,
|
|
MAX_FAST_REFS))
|
|
{
|
|
/* Insert references */
|
|
if (!ExInsertFastReference(&CallBack->RoutineBlock, CallbackBlock))
|
|
{
|
|
/* Backdown the rundown acquire */
|
|
ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect,
|
|
MAX_FAST_REFS);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return the callback block */
|
|
return CallbackBlock;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack,
|
|
IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock,
|
|
IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock)
|
|
{
|
|
EX_FAST_REF OldValue;
|
|
PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock;
|
|
ULONG Count;
|
|
|
|
/* Check that we have a new block */
|
|
if (NewBlock)
|
|
{
|
|
/* Acquire rundown */
|
|
if (!ExfAcquireRundownProtectionEx(&NewBlock->RundownProtect,
|
|
MAX_FAST_REFS + 1))
|
|
{
|
|
/* This should never happen */
|
|
ASSERTMSG("Callback block is already undergoing rundown", FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Do the swap */
|
|
OldValue = ExCompareSwapFastReference(&CallBack->RoutineBlock,
|
|
NewBlock,
|
|
OldBlock);
|
|
|
|
/* Get the routine block */
|
|
CallbackBlock = ExGetObjectFastReference(OldValue);
|
|
Count = ExGetCountFastReference(OldValue);
|
|
|
|
/* Make sure the swap worked */
|
|
if (CallbackBlock == OldBlock)
|
|
{
|
|
/* Make sure we replaced a valid pointer */
|
|
if (CallbackBlock)
|
|
{
|
|
/* Acquire the flush lock and immediately release it */
|
|
KeEnterCriticalRegion();
|
|
ExWaitOnPushLock(&ExpCallBackFlush);
|
|
|
|
/* Release rundown protection */
|
|
KeLeaveCriticalRegion();
|
|
ExfReleaseRundownProtectionEx(&CallbackBlock->RundownProtect,
|
|
Count + 1);
|
|
}
|
|
|
|
/* Compare worked */
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* It failed, check if we had a block */
|
|
if (NewBlock)
|
|
{
|
|
/* We did, remove the references that we had added */
|
|
ExfReleaseRundownProtectionEx(&NewBlock->RundownProtect,
|
|
MAX_FAST_REFS + 1);
|
|
}
|
|
|
|
/* Return failure */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ExpDeleteCallback(IN PVOID Object)
|
|
{
|
|
/* Sanity check */
|
|
ASSERT(IsListEmpty(&((PCALLBACK_OBJECT)Object)->RegisteredCallbacks));
|
|
}
|
|
|
|
/*++
|
|
* @name ExpInitializeCallbacks
|
|
*
|
|
* Creates the Callback Object as a valid Object Type in the Kernel.
|
|
* Internal function, subject to further review
|
|
*
|
|
* @return TRUE if the Callback Object Type was successfully created.
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
BOOLEAN
|
|
INIT_FUNCTION
|
|
NTAPI
|
|
ExpInitializeCallbacks(VOID)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING DirName = RTL_CONSTANT_STRING(L"\\Callback");
|
|
UNICODE_STRING CallbackName;
|
|
UNICODE_STRING Name;
|
|
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
|
|
HANDLE DirectoryHandle;
|
|
ULONG i;
|
|
|
|
/* Setup lightweight callback lock */
|
|
ExpCallBackFlush.Value = 0;
|
|
|
|
/* Initialize the Callback Object type */
|
|
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
|
|
RtlInitUnicodeString(&Name, L"Callback");
|
|
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
|
|
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
|
|
ObjectTypeInitializer.GenericMapping = ExpCallbackMapping;
|
|
ObjectTypeInitializer.PoolType = NonPagedPool;
|
|
ObjectTypeInitializer.DeleteProcedure = ExpDeleteCallback;
|
|
ObjectTypeInitializer.ValidAccessMask = CALLBACK_ALL_ACCESS;
|
|
Status = ObCreateObjectType(&Name,
|
|
&ObjectTypeInitializer,
|
|
NULL,
|
|
&ExCallbackObjectType);
|
|
if (!NT_SUCCESS(Status)) return FALSE;
|
|
|
|
/* Initialize the Object */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DirName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
|
|
NULL,
|
|
SePublicDefaultSd);
|
|
|
|
/* Create the Object Directory */
|
|
Status = NtCreateDirectoryObject(&DirectoryHandle,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
if (!NT_SUCCESS(Status)) return FALSE;
|
|
|
|
/* Close Handle... */
|
|
NtClose(DirectoryHandle);
|
|
|
|
/* Initialize Event used when unregistering */
|
|
KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0);
|
|
|
|
/* Default NT Kernel Callbacks. */
|
|
for (i = 0; ExpInitializeCallback[i].CallbackObject; i++)
|
|
{
|
|
/* Create the name from the structure */
|
|
RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name);
|
|
|
|
/* Initialize the Object Attributes Structure */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&CallbackName,
|
|
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
/* Create the Callback Object */
|
|
Status = ExCreateCallback(ExpInitializeCallback[i].CallbackObject,
|
|
&ObjectAttributes,
|
|
TRUE,
|
|
TRUE);
|
|
if (!NT_SUCCESS(Status)) return FALSE;
|
|
}
|
|
|
|
/* Everything successful */
|
|
return TRUE;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*++
|
|
* @name ExCreateCallback
|
|
* @implemented
|
|
*
|
|
* Opens or creates a Callback Object. Creates only if Create is true.
|
|
* Allows multiple Callback Functions to be registered only if
|
|
* AllowMultipleCallbacks is true.
|
|
* See: http://www.osronline.com/ddkx/kmarch/k102_967m.htm
|
|
* http://www.osronline.com/article.cfm?id=24
|
|
*
|
|
* @param CallbackObject
|
|
* Pointer that will receive the Callback Object.
|
|
*
|
|
* @param CallbackName
|
|
* Name of Callback
|
|
*
|
|
* @param Create
|
|
* Determines if the object will be created if it doesn't exit
|
|
*
|
|
* @param AllowMultipleCallbacks
|
|
* Determines if more then one registered callback function
|
|
* can be attached to this Callback Object.
|
|
*
|
|
* @return STATUS_SUCESS if not failed.
|
|
*
|
|
* @remarks Must be called at IRQL = PASSIVE_LEVEL
|
|
*
|
|
*--*/
|
|
NTSTATUS
|
|
NTAPI
|
|
ExCreateCallback(OUT PCALLBACK_OBJECT *CallbackObject,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN BOOLEAN Create,
|
|
IN BOOLEAN AllowMultipleCallbacks)
|
|
{
|
|
PCALLBACK_OBJECT Callback = NULL;
|
|
NTSTATUS Status;
|
|
HANDLE Handle = NULL;
|
|
PAGED_CODE();
|
|
|
|
/* Open a handle to the callback if it exists */
|
|
if (ObjectAttributes->ObjectName)
|
|
{
|
|
/* Open the handle */
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
ExCallbackObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&Handle);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, fail */
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/* We weren't able to open it...should we create it? */
|
|
if (!(NT_SUCCESS(Status)) && (Create))
|
|
{
|
|
/* Create the object */
|
|
Status = ObCreateObject(KernelMode,
|
|
ExCallbackObjectType,
|
|
ObjectAttributes,
|
|
KernelMode,
|
|
NULL,
|
|
sizeof(CALLBACK_OBJECT),
|
|
0,
|
|
0,
|
|
(PVOID *)&Callback);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Set it up */
|
|
Callback->Signature = 'llaC';
|
|
KeInitializeSpinLock(&Callback->Lock);
|
|
InitializeListHead(&Callback->RegisteredCallbacks);
|
|
Callback->AllowMultipleCallbacks = AllowMultipleCallbacks;
|
|
|
|
/* Insert the object into the object namespace */
|
|
Status = ObInsertObject(Callback,
|
|
NULL,
|
|
FILE_READ_DATA,
|
|
0,
|
|
NULL,
|
|
&Handle);
|
|
}
|
|
}
|
|
|
|
/* Check if we have success until here */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Get a pointer to the new object from the handle we just got */
|
|
Status = ObReferenceObjectByHandle(Handle,
|
|
0,
|
|
ExCallbackObjectType,
|
|
KernelMode,
|
|
(PVOID *)&Callback,
|
|
NULL);
|
|
|
|
/* Close the Handle, since we now have the pointer */
|
|
ZwClose(Handle);
|
|
}
|
|
|
|
/* Everything went fine, so return a pointer to the Object */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*CallbackObject = Callback;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
* @name ExNotifyCallback
|
|
* @implemented
|
|
*
|
|
* Calls a function pointer (a registered callback)
|
|
* See: http://www.osronline.com/ddkx/kmarch/k102_2f5e.htm
|
|
* http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt
|
|
*
|
|
* @param CallbackObject
|
|
* Which callback to call
|
|
*
|
|
* @param Argument1
|
|
* Pointer/data to send to callback function
|
|
*
|
|
* @param Argument2
|
|
* Pointer/data to send to callback function
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
VOID
|
|
NTAPI
|
|
ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject,
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2)
|
|
{
|
|
PLIST_ENTRY RegisteredCallbacks;
|
|
PCALLBACK_REGISTRATION CallbackRegistration;
|
|
KIRQL OldIrql;
|
|
|
|
/* Check if we don't have an object or registrations */
|
|
if (!(CallbackObject) ||
|
|
(IsListEmpty(&CallbackObject->RegisteredCallbacks)))
|
|
{
|
|
/* Don't notify */
|
|
return;
|
|
}
|
|
|
|
/* Acquire the Lock */
|
|
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
|
|
|
|
/* Enumerate through all the registered functions */
|
|
for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink;
|
|
RegisteredCallbacks != &CallbackObject->RegisteredCallbacks;
|
|
RegisteredCallbacks = RegisteredCallbacks->Flink)
|
|
{
|
|
/* Get a pointer to a Callback Registration from the List Entries */
|
|
CallbackRegistration = CONTAINING_RECORD(RegisteredCallbacks,
|
|
CALLBACK_REGISTRATION,
|
|
Link);
|
|
|
|
/* Don't bother doing notification if it's pending to be deleted */
|
|
if (!CallbackRegistration->UnregisterWaiting)
|
|
{
|
|
/* Mark the Callback in use, so it won't get deleted */
|
|
CallbackRegistration->Busy += 1;
|
|
|
|
/* Release the Spinlock before making the call */
|
|
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
|
|
|
|
/* Call the Registered Function */
|
|
CallbackRegistration->CallbackFunction(CallbackRegistration->
|
|
CallbackContext,
|
|
Argument1,
|
|
Argument2);
|
|
|
|
/* Get SpinLock back */
|
|
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
|
|
|
|
/* We are not in use anymore */
|
|
CallbackRegistration->Busy -= 1;
|
|
|
|
/* Check if removal is pending and we're not active */
|
|
if ((CallbackRegistration->UnregisterWaiting) &&
|
|
!(CallbackRegistration->Busy))
|
|
{
|
|
/* Signal the callback event */
|
|
KeSetEvent(&ExpCallbackEvent, 0, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release the Callback Object */
|
|
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
|
|
}
|
|
|
|
/*++
|
|
* @name ExRegisterCallback
|
|
* @implemented
|
|
*
|
|
* Allows a function to associate a callback pointer (Function) to
|
|
* a created Callback object
|
|
* See: DDK, OSR, links in ExNotifyCallback
|
|
*
|
|
* @param CallbackObject
|
|
* The Object Created with ExCreateCallBack
|
|
*
|
|
* @param CallBackFunction
|
|
* Pointer to the function to be called back
|
|
*
|
|
* @param CallBackContext
|
|
* Block of memory that can contain user-data which will be
|
|
* passed on to the callback
|
|
*
|
|
* @return A handle to a Callback Registration Structure (MSDN Documentation)
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
PVOID
|
|
NTAPI
|
|
ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject,
|
|
IN PCALLBACK_FUNCTION CallbackFunction,
|
|
IN PVOID CallbackContext)
|
|
{
|
|
PCALLBACK_REGISTRATION CallbackRegistration = NULL;
|
|
KIRQL OldIrql;
|
|
|
|
/* Sanity checks */
|
|
ASSERT(CallbackFunction);
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Create reference to Callback Object */
|
|
ObReferenceObject(CallbackObject);
|
|
|
|
/* Allocate memory for the structure */
|
|
CallbackRegistration = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(CALLBACK_REGISTRATION),
|
|
TAG_CALLBACK_REGISTRATION);
|
|
if (!CallbackRegistration)
|
|
{
|
|
/* Dereference and fail */
|
|
ObDereferenceObject (CallbackObject);
|
|
return NULL;
|
|
}
|
|
|
|
/* Create Callback Registration */
|
|
CallbackRegistration->CallbackObject = CallbackObject;
|
|
CallbackRegistration->CallbackFunction = CallbackFunction;
|
|
CallbackRegistration->CallbackContext = CallbackContext;
|
|
CallbackRegistration->Busy = 0;
|
|
CallbackRegistration->UnregisterWaiting = FALSE;
|
|
|
|
/* Acquire SpinLock */
|
|
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
|
|
|
|
/* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
|
|
if ((CallbackObject->AllowMultipleCallbacks) ||
|
|
(IsListEmpty(&CallbackObject->RegisteredCallbacks)))
|
|
{
|
|
/* Register the callback */
|
|
InsertTailList(&CallbackObject->RegisteredCallbacks,
|
|
&CallbackRegistration->Link);
|
|
|
|
/* Release SpinLock */
|
|
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
|
|
}
|
|
else
|
|
{
|
|
/* Release SpinLock */
|
|
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
|
|
|
|
/* Free the registration */
|
|
ExFreePoolWithTag(CallbackRegistration, TAG_CALLBACK_REGISTRATION);
|
|
CallbackRegistration = NULL;
|
|
|
|
/* Dereference the object */
|
|
ObDereferenceObject(CallbackObject);
|
|
}
|
|
|
|
/* Return handle to Registration Object */
|
|
return (PVOID)CallbackRegistration;
|
|
}
|
|
|
|
/*++
|
|
* @name ExUnregisterCallback
|
|
* @implemented
|
|
*
|
|
* Deregisters a CallBack
|
|
* See: DDK, OSR, links in ExNotifyCallback
|
|
*
|
|
* @param CallbackRegistration
|
|
* Callback Registration Handle
|
|
*
|
|
* @return None
|
|
*
|
|
* @remarks None
|
|
*
|
|
*--*/
|
|
VOID
|
|
NTAPI
|
|
ExUnregisterCallback(IN PVOID CallbackRegistrationHandle)
|
|
{
|
|
PCALLBACK_REGISTRATION CallbackRegistration;
|
|
PCALLBACK_OBJECT CallbackObject;
|
|
KIRQL OldIrql;
|
|
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
|
|
|
|
/* Convert Handle to valid Structure Pointer */
|
|
CallbackRegistration = (PCALLBACK_REGISTRATION)CallbackRegistrationHandle;
|
|
|
|
/* Get the Callback Object */
|
|
CallbackObject = CallbackRegistration->CallbackObject;
|
|
|
|
/* Lock the Object */
|
|
KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
|
|
|
|
/* We can't Delete the Callback if it's in use */
|
|
while (CallbackRegistration->Busy)
|
|
{
|
|
/* Let everyone else know we're unregistering */
|
|
CallbackRegistration->UnregisterWaiting = TRUE;
|
|
|
|
/* We are going to wait for the event, so the Lock isn't necessary */
|
|
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
|
|
|
|
/* Make sure the event is cleared */
|
|
KeClearEvent(&ExpCallbackEvent);
|
|
|
|
/* Wait for the Event */
|
|
KeWaitForSingleObject(&ExpCallbackEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
/* We need the Lock again */
|
|
KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
|
|
}
|
|
|
|
/* Remove the Callback */
|
|
RemoveEntryList(&CallbackRegistration->Link);
|
|
|
|
/* It's now safe to release the lock */
|
|
KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
|
|
|
|
/* Delete this registration */
|
|
ExFreePoolWithTag(CallbackRegistration, TAG_CALLBACK_REGISTRATION);
|
|
|
|
/* Remove the reference */
|
|
ObDereferenceObject(CallbackObject);
|
|
}
|
|
|
|
/* EOF */
|