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