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