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