Standardize comment headers. Patch by Trevor McCort
[reactos.git] / reactos / ntoskrnl / ex / callback.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ex/callback.c
6 * PURPOSE: Executive callbacks
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Alex Ionescu (alex@relsoft.net)
10 */
11
12
13 /*
14 * NOTE: These funtions are not implemented in NT4, but
15 * they are implemented in Win2k.
16 */
17
18 /* INCLUDES *****************************************************************/
19
20 #include <ntoskrnl.h>
21 #include <internal/callbacks.h>
22 #include <internal/debug.h>
23
24 /* FUNCTIONS *****************************************************************/
25
26 /*
27 * ExpInitializeCallbacks
28 *
29 * FUNCTION:
30 * Creates the Callback Object as a valid Object Type in the Kernel.
31 *
32 * ARGUMENTS:
33 * None
34 *
35 * RETURNS:
36 * TRUE if the Callback Object Type was successfully created.
37 */
38 VOID
39 ExpInitializeCallbacks(VOID)
40 {
41 OBJECT_ATTRIBUTES ObjectAttributes;
42 NTSTATUS Status;
43 UNICODE_STRING DirName;
44 UNICODE_STRING CallbackName;
45 HANDLE DirectoryHandle;
46 ULONG i;
47
48 /* Allocate memory for Object */
49 ExCallbackObjectType = ExAllocatePoolWithTag(NonPagedPool, sizeof(OBJECT_TYPE), CALLBACK_TAG);
50
51 /* Initialize name */
52 RtlRosInitUnicodeStringFromLiteral(&ExCallbackObjectType->TypeName,L"Callback");
53
54 /* Create the Object Type */
55 ExCallbackObjectType->Tag = CALLBACK_TAG;
56 ExCallbackObjectType->TotalObjects = 0;
57 ExCallbackObjectType->TotalHandles = 0;
58 ExCallbackObjectType->PeakObjects = 0;
59 ExCallbackObjectType->PeakHandles = 0;
60 ExCallbackObjectType->PagedPoolCharge = 0;
61 ExCallbackObjectType->Dump = NULL;
62 ExCallbackObjectType->Open = NULL;
63 ExCallbackObjectType->Close = NULL;
64 ExCallbackObjectType->Delete = NULL;
65 ExCallbackObjectType->Parse = NULL;
66 ExCallbackObjectType->Security = NULL;
67 ExCallbackObjectType->QueryName = NULL;
68 ExCallbackObjectType->DuplicationNotify = NULL;
69 ExCallbackObjectType->OkayToClose = NULL;
70 ExCallbackObjectType->Create = NULL;
71 ExCallbackObjectType->Mapping = &ExpCallbackMapping;
72 ExCallbackObjectType->NonpagedPoolCharge = sizeof(_INT_CALLBACK_OBJECT);
73 Status = ObpCreateTypeObject(ExCallbackObjectType);
74
75 /* Fail if it wasn't created successfully */
76 if (!NT_SUCCESS(Status))
77 {
78 return;
79 }
80
81 /* Initialize the Object */
82 RtlRosInitUnicodeStringFromLiteral(&DirName, L"\\Callback" );
83 InitializeObjectAttributes(
84 &ObjectAttributes,
85 &DirName,
86 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
87 NULL,
88 NULL
89 );
90
91 /* Create the Object Directory */
92 Status = NtCreateDirectoryObject(
93 &DirectoryHandle,
94 DIRECTORY_ALL_ACCESS,
95 &ObjectAttributes
96 );
97
98 /* Fail if couldn't create */
99 if (!NT_SUCCESS(Status))
100 {
101 return;
102 }
103
104 /* Close Handle... */
105 NtClose(DirectoryHandle);
106
107 /* Initialize Event used when unregistering */
108 KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0);
109
110 /* Default NT Kernel Callbacks. */
111 for (i=0; ExpInitializeCallback[i].CallbackObject; i++)
112 {
113 /* Create the name from the structure */
114 RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name);
115
116 /* Initialize the Object Attributes Structure */
117 InitializeObjectAttributes(
118 &ObjectAttributes,
119 &CallbackName,
120 OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
121 NULL,
122 NULL
123 );
124
125 /* Create the Callback Object */
126 Status = ExCreateCallback(
127 (PCALLBACK_OBJECT*)&(ExpInitializeCallback[i].CallbackObject),
128 &ObjectAttributes,
129 TRUE,
130 TRUE
131 );
132
133 /* Make sure Global Callbacks have been created */
134 if (!NT_SUCCESS(Status))
135 {
136 return;
137 }
138 }
139 /* Everything successful */
140 }
141
142 /*
143 * ExCreateCallback
144 *
145 * FUNCTION:
146 * Opens or creates a Callback Object. Creates only if Create is true.
147 * Allows multiple Callback Functions to be registred only if AllowMultipleCallbacks
148 * is true.
149 *
150 * ARGUMENTS:
151 * CallbackObject = Pointer that will receive the Callback Object.
152 * CallbackName = Name of Callback
153 * Create = Determines if the object will be created if it doesn't exit
154 * AllowMultipleCallbacks = Determines if more then one registered callback function
155 * can be attached to this Callback Object.
156 *
157 * RETURNS:
158 * STATUS_SUCESS if not failed.
159 *
160 * @implemented
161 */
162 NTSTATUS
163 STDCALL
164 ExCreateCallback(
165 OUT PCALLBACK_OBJECT *CallbackObject,
166 IN POBJECT_ATTRIBUTES ObjectAttributes,
167 IN BOOLEAN Create,
168 IN BOOLEAN AllowMultipleCallbacks
169 )
170 {
171 PINT_CALLBACK_OBJECT Callback;
172 NTSTATUS Status;
173 HANDLE Handle;
174
175 /* Open a handle to the callback if it exists */
176 if (ObjectAttributes->ObjectName)
177 {
178 Status = ObOpenObjectByName(ObjectAttributes,
179 ExCallbackObjectType,
180 NULL,
181 KernelMode,
182 0,
183 NULL,
184 &Handle);
185 }
186 else
187 {
188 Status = STATUS_UNSUCCESSFUL;
189 }
190
191 /* We weren't able to open it...should we create it? */
192 if(!NT_SUCCESS(Status) && Create )
193 {
194 Status = ObCreateObject(KernelMode,
195 ExCallbackObjectType,
196 ObjectAttributes,
197 KernelMode,
198 NULL,
199 sizeof(_INT_CALLBACK_OBJECT),
200 0,
201 0,
202 (PVOID *)&Callback );
203
204 /* We Created it...let's initialize the structure now */
205 if(NT_SUCCESS(Status))
206 {
207 KeInitializeSpinLock (&Callback->Lock); /* SpinLock */
208 InitializeListHead(&Callback->RegisteredCallbacks); /* Callback Entries */
209 Callback->AllowMultipleCallbacks = AllowMultipleCallbacks; /* Multiple Callbacks */
210 Status = ObInsertObject ( /* Create the object */
211 Callback,
212 NULL,
213 FILE_READ_DATA,
214 0,
215 NULL,
216 &Handle );
217 }
218 }
219 if(NT_SUCCESS(Status))
220 {
221
222 /* Get a pointer to the new object from the handle we just got */
223 Status = ObReferenceObjectByHandle (
224 Handle,
225 0,
226 ExCallbackObjectType,
227 KernelMode,
228 (PVOID)&Callback,
229 NULL
230 );
231 /* Close the Handle, since we now have the pointer */
232 ZwClose(Handle);
233 }
234
235 /* Everything went fine, so return a pointer to the Object */
236 if (NT_SUCCESS(Status))
237 {
238 *CallbackObject = (PCALLBACK_OBJECT)Callback;
239 }
240 return Status;
241 }
242
243 /*
244 * ExNotifyCallback
245 *
246 * FUNCTION:
247 * Calls a function pointer (a registered callback)
248 *
249 * ARGUMENTS:
250 * CallbackObject - Which callback to call
251 * Argument1 - Pointer/data to send to callback function
252 * Argument2 - Pointer/data to send to callback function
253 *
254 * RETURNS:
255 * Nothing
256 *
257 * @implemented
258 */
259 VOID
260 STDCALL
261 ExNotifyCallback(
262 IN PCALLBACK_OBJECT OpaqueCallbackObject,
263 IN PVOID Argument1,
264 IN PVOID Argument2
265 )
266 {
267 PINT_CALLBACK_OBJECT CallbackObject = (PINT_CALLBACK_OBJECT)OpaqueCallbackObject;
268 PLIST_ENTRY RegisteredCallbacks;
269 PCALLBACK_REGISTRATION CallbackRegistration;
270 KIRQL OldIrql;
271
272 /* Acquire the Lock */
273 OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
274
275 /* Enumerate through all the registered functions */
276 for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink;
277 RegisteredCallbacks != &CallbackObject->RegisteredCallbacks;
278 RegisteredCallbacks = RegisteredCallbacks->Flink)
279 {
280
281 /* Get a pointer to a Callback Registration from the List Entries */
282 CallbackRegistration = CONTAINING_RECORD ( RegisteredCallbacks,
283 CALLBACK_REGISTRATION,
284 RegisteredCallbacks);
285
286 /* Don't bother doing Callback Notification if it's pending to be deleted */
287 if (!CallbackRegistration->PendingDeletion)
288 {
289
290 /* Mark the Callback in use, so it won't get deleted while we are calling it */
291 CallbackRegistration->InUse += 1;
292
293 /* Release the Spinlock before making the call */
294 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
295
296 /* Call the Registered Function */
297 CallbackRegistration->CallbackFunction (
298 CallbackRegistration->CallbackContext,
299 Argument1,
300 Argument2
301 );
302
303 /* Get SpinLock back */
304 OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
305
306 /* We are not in use anymore */
307 CallbackRegistration->InUse -= 1;
308
309 /* If another instance of this function isn't running and deletion is pending, signal the event */
310 if (CallbackRegistration->PendingDeletion && CallbackRegistration->InUse == 0)
311 {
312 KeSetEvent(&ExpCallbackEvent, 0, FALSE);
313 }
314 }
315 }
316 /* Unsynchronize and release the Callback Object */
317 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
318 }
319
320 /*
321 * ExRegisterCallback
322 *
323 * FUNCTION:
324 * Allows a function to associate a callback pointer (Function)
325 * to a created Callback object
326 *
327 * ARGUMENTS:
328 * CallbackObject = The Object Created with ExCreateCallBack
329 * CallBackFunction = Pointer to the function to be called back
330 * CallBackContext = Block of memory that can contain user-data
331 * which will be passed on to the callback
332 *
333 * RETURNS:
334 * A handle to a Callback Registration Structure (MSDN Documentation)
335 *
336 * @implemented
337 */
338 PVOID
339 STDCALL
340 ExRegisterCallback(
341 IN PCALLBACK_OBJECT OpaqueCallbackObject,
342 IN PCALLBACK_FUNCTION CallbackFunction,
343 IN PVOID CallbackContext
344 )
345 {
346 PINT_CALLBACK_OBJECT CallbackObject = (PINT_CALLBACK_OBJECT)OpaqueCallbackObject;
347 PCALLBACK_REGISTRATION CallbackRegistration = NULL;
348 KIRQL OldIrql;
349
350 /* Create reference to Callback Object */
351 ObReferenceObject (CallbackObject);
352
353 /* Allocate memory for the structure */
354 CallbackRegistration = ExAllocatePoolWithTag(
355 NonPagedPool,
356 sizeof(CallbackRegistration),
357 CALLBACK_TAG
358 );
359 /* Fail if memory allocation failed */
360 if(!CallbackRegistration)
361 {
362 ObDereferenceObject (CallbackObject);
363 return NULL;
364 }
365
366 /* Create Callback Registration */
367 CallbackRegistration->CallbackObject = CallbackObject; /* When unregistering, drivers send a handle to the Registration, not the object... */
368 CallbackRegistration->CallbackFunction = CallbackFunction; /* NotifyCallback uses Objects, so this needs to be here in order to call the registered functions */
369 CallbackRegistration->CallbackContext = CallbackContext; /* The documented NotifyCallback returns the Context, so we must save this somewhere */
370
371 /* Acquire SpinLock */
372 OldIrql = KfAcquireSpinLock (&CallbackObject->Lock);
373
374 /* Add Callback if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
375 if(CallbackObject->AllowMultipleCallbacks || IsListEmpty(&CallbackObject->RegisteredCallbacks))
376 {
377 InsertTailList(&CallbackObject->RegisteredCallbacks,&CallbackRegistration->RegisteredCallbacks);
378 }
379 else
380 {
381 ExFreePool(CallbackRegistration);
382 CallbackRegistration = NULL;
383 }
384
385 /* Release SpinLock */
386 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
387
388 /* Return handle to Registration Object */
389 return (PVOID) CallbackRegistration;
390 }
391
392 /*
393 * ExUnregisterCallback
394 *
395 * FUNCTION:
396 * Deregisters a CallBack
397 *
398 * ARGUMENTS:
399 * CallbackRegistration = Callback Registration Handle
400 *
401 * RETURNS:
402 * Nothing
403 *
404 * @implemented
405 */
406 VOID STDCALL
407 ExUnregisterCallback(
408 IN PVOID CallbackRegistrationHandle
409 )
410 {
411 PCALLBACK_REGISTRATION CallbackRegistration;
412 PINT_CALLBACK_OBJECT CallbackObject;
413 KIRQL OldIrql;
414
415 /* Convert Handle to valid Structure Pointer */
416 CallbackRegistration = (PCALLBACK_REGISTRATION) CallbackRegistrationHandle;
417
418 /* Get the Callback Object */
419 CallbackObject = CallbackRegistration->CallbackObject;
420
421 /* Lock the Object */
422 OldIrql = KfAcquireSpinLock (&CallbackObject->Lock);
423
424 /* We can't Delete the Callback if it's in use, because this would create a call towards a null pointer => crash */
425 while (CallbackRegistration->InUse)
426 {
427
428 /* Similarly, we also don't want to wait ages for all pending callbacks to be called */
429 CallbackRegistration->PendingDeletion = TRUE;
430
431 /* We are going to wait for the event, so the Lock isn't necessary */
432 KfReleaseSpinLock (&CallbackObject->Lock, OldIrql);
433
434 /* Make sure the event is cleared */
435 KeClearEvent (&ExpCallbackEvent);
436
437 /* Wait for the Event */
438 KeWaitForSingleObject (
439 &ExpCallbackEvent,
440 Executive,
441 KernelMode,
442 FALSE,
443 NULL
444 );
445
446 /* We need the Lock again */
447 OldIrql = KfAcquireSpinLock(&CallbackObject->Lock);
448 }
449
450 /* Remove the Callback */
451 RemoveEntryList(&CallbackRegistration->RegisteredCallbacks);
452
453 /* It's now safe to release the lock */
454 KfReleaseSpinLock(&CallbackObject->Lock, OldIrql);
455
456 /* Delete this registration */
457 ExFreePool(CallbackRegistration);
458
459 /* Remove the reference */
460 ObDereferenceObject(CallbackObject);
461 }
462
463 /* EOF */