Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689/* * 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 *********************************************************/
VOIDNTAPIExInitializeCallBack(IN OUT PEX_CALLBACK Callback){ /* Initialize the fast reference */ ExInitializeFastReference(&Callback->RoutineBlock, NULL);}
PEX_CALLBACK_ROUTINE_BLOCKNTAPIExAllocateCallBack(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;}
VOIDNTAPIExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock){ /* Just free it from memory */ ExFreePoolWithTag(CallbackBlock, TAG_CALLBACK_ROUTINE_BLOCK);}
VOIDNTAPIExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock){ /* Wait on the rundown */ ExWaitForRundownProtectionRelease(&CallbackBlock->RundownProtect);}
PEX_CALLBACK_FUNCTIONNTAPIExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock){ /* Return the function */ return CallbackBlock->Function;}
PVOIDNTAPIExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock){ /* Return the context */ return CallbackBlock->Context;}
VOIDNTAPIExDereferenceCallBackBlock(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_BLOCKNTAPIExReferenceCallBackBlock(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;}
BOOLEANNTAPIExCompareExchangeCallBack(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\n", 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; }}
VOIDNTAPIExpDeleteCallback(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 * *--*/CODE_SEG("INIT")BOOLEANNTAPIExpInitializeCallbacks(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: https://web.archive.org/web/20081230235552/http://www.osronline.com/DDKx/kmarch/k102_967m.htm * https://www.osronline.com/article.cfm%5eid=24.htm * * @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 * *--*/NTSTATUSNTAPIExCreateCallback(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: https://web.archive.org/web/20090106214158/http://www.osronline.com/DDKx/kmarch/k102_2f5e.htm * http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt (DEAD_LINK) * * @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 * *--*/VOIDNTAPIExNotifyCallback(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 * *--*/PVOIDNTAPIExRegisterCallback(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 * *--*/VOIDNTAPIExUnregisterCallback(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 */