* Sync up to trunk head (r60691).
[reactos.git] / ntoskrnl / ob / obref.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obref.c
5 * PURPOSE: Manages the referencing and de-referencing of all Objects,
6 * as well as the Object Fast Reference implementation.
7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
8 * Eric Kohl
9 * Thomas Weidenmueller (w3seek@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 /* PRIVATE FUNCTIONS *********************************************************/
19
20 BOOLEAN
21 FASTCALL
22 ObReferenceObjectSafe(IN PVOID Object)
23 {
24 POBJECT_HEADER ObjectHeader;
25 LONG OldValue, NewValue;
26
27 /* Get the object header */
28 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
29
30 /* Get the current reference count and fail if it's zero */
31 OldValue = ObjectHeader->PointerCount;
32 if (!OldValue) return FALSE;
33
34 /* Start reference loop */
35 do
36 {
37 /* Increase the reference count */
38 NewValue = InterlockedCompareExchange(&ObjectHeader->PointerCount,
39 OldValue + 1,
40 OldValue);
41 if (OldValue == NewValue) return TRUE;
42
43 /* Keep looping */
44 OldValue = NewValue;
45 } while (OldValue);
46
47 /* If we got here, then the reference count is now 0 */
48 return FALSE;
49 }
50
51 VOID
52 NTAPI
53 ObpDeferObjectDeletion(IN POBJECT_HEADER Header)
54 {
55 PVOID Entry;
56
57 /* Loop while trying to update the list */
58 do
59 {
60 /* Get the current entry */
61 Entry = ObpReaperList;
62
63 /* Link our object to the list */
64 Header->NextToFree = Entry;
65
66 /* Update the list */
67 } while (InterlockedCompareExchangePointer(&ObpReaperList,
68 Header,
69 Entry) != Entry);
70
71 /* Queue the work item if needed */
72 if (!Entry) ExQueueWorkItem(&ObpReaperWorkItem, CriticalWorkQueue);
73 }
74
75 LONG
76 FASTCALL
77 ObReferenceObjectEx(IN PVOID Object,
78 IN LONG Count)
79 {
80 /* Increment the reference count and return the count now */
81 return InterlockedExchangeAdd(&OBJECT_TO_OBJECT_HEADER(Object)->
82 PointerCount,
83 Count) + Count;
84 }
85
86 LONG
87 FASTCALL
88 ObDereferenceObjectEx(IN PVOID Object,
89 IN LONG Count)
90 {
91 POBJECT_HEADER Header;
92 LONG NewCount;
93
94 /* Extract the object header */
95 Header = OBJECT_TO_OBJECT_HEADER(Object);
96
97 /* Check whether the object can now be deleted. */
98 NewCount = InterlockedExchangeAdd(&Header->PointerCount, -Count) - Count;
99 if (!NewCount) ObpDeferObjectDeletion(Header);
100
101 /* Return the current count */
102 return NewCount;
103 }
104
105 VOID
106 FASTCALL
107 ObInitializeFastReference(IN PEX_FAST_REF FastRef,
108 IN PVOID Object OPTIONAL)
109 {
110 /* Check if we were given an object and reference it 7 times */
111 if (Object) ObReferenceObjectEx(Object, MAX_FAST_REFS);
112
113 /* Setup the fast reference */
114 ExInitializeFastReference(FastRef, Object);
115 }
116
117 PVOID
118 FASTCALL
119 ObFastReferenceObjectLocked(IN PEX_FAST_REF FastRef)
120 {
121 PVOID Object;
122 EX_FAST_REF OldValue = *FastRef;
123
124 /* Get the object and reference it slowly */
125 Object = ExGetObjectFastReference(OldValue);
126 if (Object) ObReferenceObject(Object);
127 return Object;
128 }
129
130 PVOID
131 FASTCALL
132 ObFastReferenceObject(IN PEX_FAST_REF FastRef)
133 {
134 EX_FAST_REF OldValue;
135 ULONG_PTR Count;
136 PVOID Object;
137
138 /* Reference the object and get it pointer */
139 OldValue = ExAcquireFastReference(FastRef);
140 Object = ExGetObjectFastReference(OldValue);
141
142 /* Check how many references are left */
143 Count = ExGetCountFastReference(OldValue);
144
145 /* Check if the reference count is over 1 */
146 if (Count > 1) return Object;
147
148 /* Check if the reference count has reached 0 */
149 if (!Count) return NULL;
150
151 /* Otherwise, reference the object 7 times */
152 ObReferenceObjectEx(Object, MAX_FAST_REFS);
153
154 /* Now update the reference count */
155 if (!ExInsertFastReference(FastRef, Object))
156 {
157 /* We failed: completely dereference the object */
158 ObDereferenceObjectEx(Object, MAX_FAST_REFS);
159 }
160
161 /* Return the Object */
162 return Object;
163 }
164
165 VOID
166 FASTCALL
167 ObFastDereferenceObject(IN PEX_FAST_REF FastRef,
168 IN PVOID Object)
169 {
170 /* Release a fast reference. If this failed, use the slow path */
171 if (!ExReleaseFastReference(FastRef, Object)) ObDereferenceObject(Object);
172 }
173
174 PVOID
175 FASTCALL
176 ObFastReplaceObject(IN PEX_FAST_REF FastRef,
177 PVOID Object)
178 {
179 EX_FAST_REF OldValue;
180 PVOID OldObject;
181 ULONG Count;
182
183 /* Check if we were given an object and reference it 7 times */
184 if (Object) ObReferenceObjectEx(Object, MAX_FAST_REFS);
185
186 /* Do the swap */
187 OldValue = ExSwapFastReference(FastRef, Object);
188 OldObject = ExGetObjectFastReference(OldValue);
189
190 /* Check if we had an active object and dereference it */
191 Count = ExGetCountFastReference(OldValue);
192 if ((OldObject) && (Count)) ObDereferenceObjectEx(OldObject, Count);
193
194 /* Return the old object */
195 return OldObject;
196 }
197
198 /* PUBLIC FUNCTIONS *********************************************************/
199
200 LONG_PTR
201 FASTCALL
202 ObfReferenceObject(IN PVOID Object)
203 {
204 ASSERT(Object);
205
206 /* Get the header and increment the reference count */
207 return InterlockedIncrement(&OBJECT_TO_OBJECT_HEADER(Object)->PointerCount);
208 }
209
210 LONG_PTR
211 FASTCALL
212 ObfDereferenceObject(IN PVOID Object)
213 {
214 POBJECT_HEADER Header;
215 LONG_PTR OldCount;
216
217 /* Extract the object header */
218 Header = OBJECT_TO_OBJECT_HEADER(Object);
219
220 if (Header->PointerCount < Header->HandleCount)
221 {
222 DPRINT1("Misbehaving object: %wZ\n", &Header->Type->Name);
223 return Header->PointerCount;
224 }
225
226 /* Check whether the object can now be deleted. */
227 OldCount = InterlockedDecrement(&Header->PointerCount);
228 if (!OldCount)
229 {
230 /* Sanity check */
231 ASSERT(Header->HandleCount == 0);
232
233 /* Check if APCs are still active */
234 if (!KeAreAllApcsDisabled())
235 {
236 /* Remove the object */
237 ObpDeleteObject(Object, FALSE);
238 }
239 else
240 {
241 /* Add us to the deferred deletion list */
242 ObpDeferObjectDeletion(Header);
243 }
244 }
245
246 /* Return the old count */
247 return OldCount;
248 }
249
250 VOID
251 NTAPI
252 ObDereferenceObjectDeferDelete(IN PVOID Object)
253 {
254 POBJECT_HEADER Header = OBJECT_TO_OBJECT_HEADER(Object);
255
256 /* Check whether the object can now be deleted. */
257 if (!InterlockedDecrement(&Header->PointerCount))
258 {
259 /* Add us to the deferred deletion list */
260 ObpDeferObjectDeletion(Header);
261 }
262 }
263
264 #undef ObDereferenceObject
265 VOID
266 NTAPI
267 ObDereferenceObject(IN PVOID Object)
268 {
269 /* Call the fastcall function */
270 ObfDereferenceObject(Object);
271 }
272
273 NTSTATUS
274 NTAPI
275 ObReferenceObjectByPointer(IN PVOID Object,
276 IN ACCESS_MASK DesiredAccess,
277 IN POBJECT_TYPE ObjectType,
278 IN KPROCESSOR_MODE AccessMode)
279 {
280 POBJECT_HEADER Header;
281
282 /* Get the header */
283 Header = OBJECT_TO_OBJECT_HEADER(Object);
284
285 /*
286 * Validate object type if the call is for UserMode.
287 * NOTE: Unless it's a symbolic link (Caz Yokoyama [MSFT])
288 */
289 if ((Header->Type != ObjectType) && ((AccessMode != KernelMode) ||
290 (ObjectType == ObSymbolicLinkType)))
291 {
292 /* Invalid type */
293 return STATUS_OBJECT_TYPE_MISMATCH;
294 }
295
296 /* Increment the reference count and return success */
297 InterlockedIncrement(&Header->PointerCount);
298 return STATUS_SUCCESS;
299 }
300
301 NTSTATUS
302 NTAPI
303 ObReferenceObjectByName(IN PUNICODE_STRING ObjectPath,
304 IN ULONG Attributes,
305 IN PACCESS_STATE PassedAccessState,
306 IN ACCESS_MASK DesiredAccess,
307 IN POBJECT_TYPE ObjectType,
308 IN KPROCESSOR_MODE AccessMode,
309 IN OUT PVOID ParseContext,
310 OUT PVOID* ObjectPtr)
311 {
312 PVOID Object = NULL;
313 UNICODE_STRING ObjectName;
314 NTSTATUS Status;
315 OBP_LOOKUP_CONTEXT Context;
316 AUX_ACCESS_DATA AuxData;
317 ACCESS_STATE AccessState;
318 PAGED_CODE();
319
320 /* Fail quickly */
321 if (!ObjectPath) return STATUS_OBJECT_NAME_INVALID;
322
323 /* Capture the name */
324 Status = ObpCaptureObjectName(&ObjectName, ObjectPath, AccessMode, TRUE);
325 if (!NT_SUCCESS(Status)) return Status;
326
327 /* We also need a valid name after capture */
328 if (!ObjectName.Length) return STATUS_OBJECT_NAME_INVALID;
329
330 /* Check if we didn't get an access state */
331 if (!PassedAccessState)
332 {
333 /* Use our built-in access state */
334 PassedAccessState = &AccessState;
335 Status = SeCreateAccessState(&AccessState,
336 &AuxData,
337 DesiredAccess,
338 &ObjectType->TypeInfo.GenericMapping);
339 if (!NT_SUCCESS(Status)) goto Quickie;
340 }
341
342 /* Find the object */
343 *ObjectPtr = NULL;
344 Status = ObpLookupObjectName(NULL,
345 &ObjectName,
346 Attributes,
347 ObjectType,
348 AccessMode,
349 ParseContext,
350 NULL,
351 NULL,
352 PassedAccessState,
353 &Context,
354 &Object);
355
356 /* Cleanup after lookup */
357 ObpReleaseLookupContext(&Context);
358
359 /* Check if the lookup succeeded */
360 if (NT_SUCCESS(Status))
361 {
362 /* Check if access is allowed */
363 if (ObpCheckObjectReference(Object,
364 PassedAccessState,
365 FALSE,
366 AccessMode,
367 &Status))
368 {
369 /* Return the object */
370 *ObjectPtr = Object;
371 }
372 }
373
374 /* Free the access state */
375 if (PassedAccessState == &AccessState)
376 {
377 SeDeleteAccessState(PassedAccessState);
378 }
379
380 Quickie:
381 /* Free the captured name if we had one, and return status */
382 ObpFreeObjectNameBuffer(&ObjectName);
383 return Status;
384 }
385
386 NTSTATUS
387 NTAPI
388 ObReferenceObjectByHandle(IN HANDLE Handle,
389 IN ACCESS_MASK DesiredAccess,
390 IN POBJECT_TYPE ObjectType,
391 IN KPROCESSOR_MODE AccessMode,
392 OUT PVOID* Object,
393 OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
394 {
395 PHANDLE_TABLE_ENTRY HandleEntry;
396 POBJECT_HEADER ObjectHeader;
397 ACCESS_MASK GrantedAccess;
398 ULONG Attributes;
399 PEPROCESS CurrentProcess;
400 PVOID HandleTable;
401 PETHREAD CurrentThread;
402 NTSTATUS Status;
403 PAGED_CODE();
404
405 /* Assume failure */
406 *Object = NULL;
407
408 /* Check if this is a special handle */
409 if (HandleToLong(Handle) < 0)
410 {
411 /* Check if this is the current process */
412 if (Handle == NtCurrentProcess())
413 {
414 /* Check if this is the right object type */
415 if ((ObjectType == PsProcessType) || !(ObjectType))
416 {
417 /* Get the current process and granted access */
418 CurrentProcess = PsGetCurrentProcess();
419 GrantedAccess = CurrentProcess->GrantedAccess;
420
421 /* Validate access */
422 /* ~GrantedAccess = RefusedAccess.*/
423 /* ~GrantedAccess & DesiredAccess = list of refused bits. */
424 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */
425 if ((AccessMode == KernelMode) ||
426 !(~GrantedAccess & DesiredAccess))
427 {
428 /* Check if the caller wanted handle information */
429 if (HandleInformation)
430 {
431 /* Return it */
432 HandleInformation->HandleAttributes = 0;
433 HandleInformation->GrantedAccess = GrantedAccess;
434 }
435
436 /* Reference ourselves */
437 ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess);
438 InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
439
440 /* Return the pointer */
441 *Object = CurrentProcess;
442 ASSERT(*Object != NULL);
443 Status = STATUS_SUCCESS;
444 }
445 else
446 {
447 /* Access denied */
448 Status = STATUS_ACCESS_DENIED;
449 }
450 }
451 else
452 {
453 /* The caller used this special handle value with a non-process type */
454 Status = STATUS_OBJECT_TYPE_MISMATCH;
455 }
456
457 /* Return the status */
458 return Status;
459 }
460 else if (Handle == NtCurrentThread())
461 {
462 /* Check if this is the right object type */
463 if ((ObjectType == PsThreadType) || !(ObjectType))
464 {
465 /* Get the current process and granted access */
466 CurrentThread = PsGetCurrentThread();
467 GrantedAccess = CurrentThread->GrantedAccess;
468
469 /* Validate access */
470 /* ~GrantedAccess = RefusedAccess.*/
471 /* ~GrantedAccess & DesiredAccess = list of refused bits. */
472 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */
473 if ((AccessMode == KernelMode) ||
474 !(~GrantedAccess & DesiredAccess))
475 {
476 /* Check if the caller wanted handle information */
477 if (HandleInformation)
478 {
479 /* Return it */
480 HandleInformation->HandleAttributes = 0;
481 HandleInformation->GrantedAccess = GrantedAccess;
482 }
483
484 /* Reference ourselves */
485 ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread);
486 InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
487
488 /* Return the pointer */
489 *Object = CurrentThread;
490 ASSERT(*Object != NULL);
491 Status = STATUS_SUCCESS;
492 }
493 else
494 {
495 /* Access denied */
496 Status = STATUS_ACCESS_DENIED;
497 }
498 }
499 else
500 {
501 /* The caller used this special handle value with a non-process type */
502 Status = STATUS_OBJECT_TYPE_MISMATCH;
503 }
504
505 /* Return the status */
506 return Status;
507 }
508 else if (AccessMode == KernelMode)
509 {
510 /* Use the kernel handle table and get the actual handle value */
511 Handle = ObKernelHandleToHandle(Handle);
512 HandleTable = ObpKernelHandleTable;
513 }
514 else
515 {
516 /* Invalid access, fail */
517 return STATUS_INVALID_HANDLE;
518 }
519 }
520 else
521 {
522 /* Otherwise use this process's handle table */
523 HandleTable = PsGetCurrentProcess()->ObjectTable;
524 }
525
526 /* Enter a critical region while we touch the handle table */
527 ASSERT(HandleTable != NULL);
528 KeEnterCriticalRegion();
529
530 /* Get the handle entry */
531 HandleEntry = ExMapHandleToPointer(HandleTable, Handle);
532 if (HandleEntry)
533 {
534 /* Get the object header and validate the type*/
535 ObjectHeader = ObpGetHandleObject(HandleEntry);
536 if (!(ObjectType) || (ObjectType == ObjectHeader->Type))
537 {
538 /* Get the granted access and validate it */
539 GrantedAccess = HandleEntry->GrantedAccess;
540
541 /* Validate access */
542 /* ~GrantedAccess = RefusedAccess.*/
543 /* ~GrantedAccess & DesiredAccess = list of refused bits. */
544 /* !(~GrantedAccess & DesiredAccess) == TRUE means ALL requested rights are granted */
545 if ((AccessMode == KernelMode) ||
546 !(~GrantedAccess & DesiredAccess))
547 {
548 /* Reference the object directly since we have its header */
549 InterlockedIncrement(&ObjectHeader->PointerCount);
550
551 /* Mask out the internal attributes */
552 Attributes = HandleEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES;
553
554 /* Check if the caller wants handle information */
555 if (HandleInformation)
556 {
557 /* Fill out the information */
558 HandleInformation->HandleAttributes = Attributes;
559 HandleInformation->GrantedAccess = GrantedAccess;
560 }
561
562 /* Return the pointer */
563 *Object = &ObjectHeader->Body;
564
565 /* Unlock the handle */
566 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
567 KeLeaveCriticalRegion();
568
569 /* Return success */
570 ASSERT(*Object != NULL);
571 return STATUS_SUCCESS;
572 }
573 else
574 {
575 /* Requested access failed */
576 DPRINT("Rights not granted: %x\n", ~GrantedAccess & DesiredAccess);
577 Status = STATUS_ACCESS_DENIED;
578 }
579 }
580 else
581 {
582 /* Invalid object type */
583 Status = STATUS_OBJECT_TYPE_MISMATCH;
584 }
585
586 /* Unlock the entry */
587 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
588 }
589 else
590 {
591 /* Invalid handle */
592 Status = STATUS_INVALID_HANDLE;
593 }
594
595 /* Return failure status */
596 KeLeaveCriticalRegion();
597 *Object = NULL;
598 return Status;
599 }
600
601 /* EOF */