2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/handle.c
5 * PURPOSE: Generic Executive Handle Tables
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller <w3seek@reactos.com>
10 /* INCLUDES ******************************************************************/
16 /* GLOBALS *******************************************************************/
18 LIST_ENTRY HandleTableListHead
;
19 EX_PUSH_LOCK HandleTableListLock
;
20 #define SizeOfHandle(x) (sizeof(HANDLE) * (x))
21 #define INDEX_TO_HANDLE_VALUE(x) ((x) << HANDLE_TAG_BITS)
23 /* PRIVATE FUNCTIONS *********************************************************/
28 ExpInitializeHandleTables(VOID
)
30 /* Initialize the list of handle tables and the lock */
31 InitializeListHead(&HandleTableListHead
);
32 ExInitializePushLock(&HandleTableListLock
);
37 ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable
,
42 PHANDLE_TABLE_ENTRY HandleArray
, Entry
;
45 /* Clear the tag bits */
48 /* Check if the handle is in the allocated range */
49 if (Handle
.Value
>= HandleTable
->NextHandleNeedingPool
)
54 /* Get the table code */
55 TableBase
= HandleTable
->TableCode
;
57 /* Extract the table level and actual table base */
58 TableLevel
= (ULONG
)(TableBase
& 3);
61 PointerArray
= (PVOID
*)TableBase
;
62 HandleArray
= (PHANDLE_TABLE_ENTRY
)TableBase
;
64 /* Check what level we're running at */
69 /* Get the mid level pointer array */
70 PointerArray
= PointerArray
[Handle
.HighIndex
];
71 ASSERT(PointerArray
!= NULL
);
76 /* Get the handle array */
77 HandleArray
= PointerArray
[Handle
.MidIndex
];
78 ASSERT(HandleArray
!= NULL
);
83 /* Get the entry using the low index */
84 Entry
= &HandleArray
[Handle
.LowIndex
];
95 /* Return the handle entry */
101 ExpAllocateTablePagedPool(IN PEPROCESS Process OPTIONAL
,
106 /* Do the allocation */
107 Buffer
= ExAllocatePoolWithTag(PagedPool
, Size
, TAG_OBJECT_TABLE
);
110 /* Clear the memory */
111 RtlZeroMemory(Buffer
, Size
);
113 /* Check if we have a process to charge quota */
116 /* FIXME: Charge quota */
120 /* Return the allocated memory */
126 ExpAllocateTablePagedPoolNoZero(IN PEPROCESS Process OPTIONAL
,
131 /* Do the allocation */
132 Buffer
= ExAllocatePoolWithTag(PagedPool
, Size
, TAG_OBJECT_TABLE
);
135 /* Check if we have a process to charge quota */
138 /* FIXME: Charge quota */
142 /* Return the allocated memory */
148 ExpFreeTablePagedPool(IN PEPROCESS Process OPTIONAL
,
152 /* Free the buffer */
153 ExFreePoolWithTag(Buffer
, TAG_OBJECT_TABLE
);
156 /* FIXME: Release quota */
162 ExpFreeLowLevelTable(IN PEPROCESS Process
,
163 IN PHANDLE_TABLE_ENTRY TableEntry
)
165 /* Check if we have an entry */
166 if (TableEntry
[0].Object
)
169 ExpFreeTablePagedPool(Process
,
170 TableEntry
[0].Object
,
172 sizeof(HANDLE_TABLE_ENTRY_INFO
));
176 ExpFreeTablePagedPool(Process
, TableEntry
, PAGE_SIZE
);
181 ExpFreeHandleTable(IN PHANDLE_TABLE HandleTable
)
183 PEPROCESS Process
= HandleTable
->QuotaProcess
;
185 ULONG_PTR TableCode
= HandleTable
->TableCode
;
186 ULONG_PTR TableBase
= TableCode
& ~3;
187 ULONG TableLevel
= (ULONG
)(TableCode
& 3);
188 PHANDLE_TABLE_ENTRY Level1
, *Level2
, **Level3
;
191 /* Check which level we're at */
194 /* Select the first level table base and just free it */
195 Level1
= (PVOID
)TableBase
;
196 ExpFreeLowLevelTable(Process
, Level1
);
198 else if (TableLevel
== 1)
200 /* Select the second level table base */
201 Level2
= (PVOID
)TableBase
;
203 /* Loop each mid level entry */
204 for (i
= 0; i
< MID_LEVEL_ENTRIES
; i
++)
206 /* Leave if we've reached the last entry */
207 if (!Level2
[i
]) break;
209 /* Free the second level table */
210 ExpFreeLowLevelTable(Process
, Level2
[i
]);
213 /* Free the second level table */
214 ExpFreeTablePagedPool(Process
, Level2
, PAGE_SIZE
);
218 /* Select the third level table base */
219 Level3
= (PVOID
)TableBase
;
221 /* Loop each high level entry */
222 for (i
= 0; i
< HIGH_LEVEL_ENTRIES
; i
++)
224 /* Leave if we've reached the last entry */
225 if (!Level3
[i
]) break;
227 /* Loop each mid level entry */
228 for (j
= 0; j
< MID_LEVEL_ENTRIES
; j
++)
230 /* Leave if we've reached the last entry */
231 if (!Level3
[i
][j
]) break;
233 /* Free the second level table */
234 ExpFreeLowLevelTable(Process
, Level3
[i
][j
]);
237 /* Free the third level table entry */
238 ExpFreeTablePagedPool(Process
, Level3
[i
], PAGE_SIZE
);
241 /* Free the third level table */
242 ExpFreeTablePagedPool(Process
,
244 SizeOfHandle(HIGH_LEVEL_ENTRIES
));
247 /* Free the actual table and check if we need to release quota */
248 ExFreePoolWithTag(HandleTable
, TAG_OBJECT_TABLE
);
257 ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable
,
259 IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
261 ULONG OldValue
, *Free
;
266 ASSERT(HandleTableEntry
->Object
== NULL
);
267 ASSERT(HandleTableEntry
== ExpLookupHandleTableEntry(HandleTable
, Handle
));
269 /* Decrement the handle count */
270 InterlockedDecrement(&HandleTable
->HandleCount
);
272 /* Mark the handle as free */
275 /* Check if we're FIFO */
276 if (!HandleTable
->StrictFIFO
)
278 /* Select a lock index */
279 LockIndex
= Handle
.Index
% 4;
281 /* Select which entry to use */
282 Free
= (HandleTable
->HandleTableLock
[LockIndex
].Locked
) ?
283 &HandleTable
->FirstFree
: &HandleTable
->LastFree
;
287 /* No need to worry about locking, take the last entry */
288 Free
= &HandleTable
->LastFree
;
291 /* Start value change loop */
294 /* Get the current value and write */
296 HandleTableEntry
->NextFreeTableEntry
= OldValue
;
297 if (InterlockedCompareExchange((PLONG
)Free
, Handle
.AsULONG
, OldValue
) == OldValue
)
299 /* Break out, we're done. Make sure the handle value makes sense */
300 ASSERT((OldValue
& FREE_HANDLE_MASK
) <
301 HandleTable
->NextHandleNeedingPool
);
309 ExpAllocateHandleTable(IN PEPROCESS Process OPTIONAL
,
312 PHANDLE_TABLE HandleTable
;
313 PHANDLE_TABLE_ENTRY HandleTableTable
, HandleEntry
;
317 /* Allocate the table */
318 HandleTable
= ExAllocatePoolWithTag(PagedPool
,
319 sizeof(HANDLE_TABLE
),
321 if (!HandleTable
) return NULL
;
323 /* Check if we have a process */
326 /* FIXME: Charge quota */
329 /* Clear the table */
330 RtlZeroMemory(HandleTable
, sizeof(HANDLE_TABLE
));
332 /* Now allocate the first level structures */
333 HandleTableTable
= ExpAllocateTablePagedPoolNoZero(Process
, PAGE_SIZE
);
334 if (!HandleTableTable
)
336 /* Failed, free the table */
337 ExFreePoolWithTag(HandleTable
, TAG_OBJECT_TABLE
);
341 /* Write the pointer to our first level structures */
342 HandleTable
->TableCode
= (ULONG_PTR
)HandleTableTable
;
344 /* Initialize the first entry */
345 HandleEntry
= &HandleTableTable
[0];
346 HandleEntry
->NextFreeTableEntry
= -2;
347 HandleEntry
->Value
= 0;
349 /* Check if this is a new table */
352 /* Go past the root entry */
355 /* Loop every low level entry */
356 for (i
= 1; i
< (LOW_LEVEL_ENTRIES
- 1); i
++)
358 /* Set up the free data */
359 HandleEntry
->Value
= 0;
360 HandleEntry
->NextFreeTableEntry
= INDEX_TO_HANDLE_VALUE(i
+ 1);
362 /* Move to the next entry */
366 /* Terminate the last entry */
367 HandleEntry
->Value
= 0;
368 HandleEntry
->NextFreeTableEntry
= 0;
369 HandleTable
->FirstFree
= INDEX_TO_HANDLE_VALUE(1);
372 /* Set the next handle needing pool after our allocated page from above */
373 HandleTable
->NextHandleNeedingPool
= INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES
);
375 /* Setup the rest of the handle table data */
376 HandleTable
->QuotaProcess
= Process
;
377 HandleTable
->UniqueProcessId
= PsGetCurrentProcess()->UniqueProcessId
;
378 HandleTable
->Flags
= 0;
380 /* Loop all the handle table locks */
381 for (i
= 0; i
< 4; i
++)
383 /* Initialize the handle table lock */
384 ExInitializePushLock(&HandleTable
->HandleTableLock
[i
]);
387 /* Initialize the contention event lock and return the lock */
388 ExInitializePushLock(&HandleTable
->HandleContentionEvent
);
394 ExpAllocateLowLevelTable(IN PHANDLE_TABLE HandleTable
,
398 PHANDLE_TABLE_ENTRY Low
, HandleEntry
;
400 /* Allocate the low level table */
401 Low
= ExpAllocateTablePagedPoolNoZero(HandleTable
->QuotaProcess
,
403 if (!Low
) return NULL
;
405 /* Setup the initial entry */
406 HandleEntry
= &Low
[0];
407 HandleEntry
->NextFreeTableEntry
= -2;
408 HandleEntry
->Value
= 0;
410 /* Check if we're initializing */
413 /* Go to the next entry and the base entry */
415 Base
= HandleTable
->NextHandleNeedingPool
+ INDEX_TO_HANDLE_VALUE(2);
417 /* Loop each entry */
419 i
< Base
+ INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES
- 2);
420 i
+= INDEX_TO_HANDLE_VALUE(1))
422 /* Free this entry and move on to the next one */
423 HandleEntry
->NextFreeTableEntry
= i
;
424 HandleEntry
->Value
= 0;
428 /* Terminate the last entry */
429 HandleEntry
->NextFreeTableEntry
= 0;
430 HandleEntry
->Value
= 0;
433 /* Return the low level table */
439 ExpAllocateMidLevelTable(IN PHANDLE_TABLE HandleTable
,
441 OUT PHANDLE_TABLE_ENTRY
*LowTableEntry
)
443 PHANDLE_TABLE_ENTRY
*Mid
, Low
;
445 /* Allocate the mid level table */
446 Mid
= ExpAllocateTablePagedPool(HandleTable
->QuotaProcess
, PAGE_SIZE
);
447 if (!Mid
) return NULL
;
449 /* Allocate a new low level for it */
450 Low
= ExpAllocateLowLevelTable(HandleTable
, DoInit
);
453 /* We failed, free the mid table */
454 ExpFreeTablePagedPool(HandleTable
->QuotaProcess
, Mid
, PAGE_SIZE
);
458 /* Link the tables and return the pointer */
460 *LowTableEntry
= Low
;
466 ExpAllocateHandleTableEntrySlow(IN PHANDLE_TABLE HandleTable
,
470 PHANDLE_TABLE_ENTRY Low
= NULL
, *Mid
, **High
, *SecondLevel
, **ThirdLevel
;
471 ULONG NewFree
, FirstFree
;
473 ULONG_PTR TableCode
= HandleTable
->TableCode
;
474 ULONG_PTR TableBase
= TableCode
& ~3;
475 ULONG TableLevel
= (ULONG
)(TableCode
& 3);
478 /* Check how many levels we already have */
481 /* Allocate a mid level, since we only have a low level */
482 Mid
= ExpAllocateMidLevelTable(HandleTable
, DoInit
, &Low
);
483 if (!Mid
) return FALSE
;
485 /* Link up the tables */
487 Mid
[0] = (PVOID
)TableBase
;
489 /* Write the new level and attempt to change the table code */
490 TableBase
= ((ULONG_PTR
)Mid
) | 1;
491 Value
= InterlockedExchangePointer((PVOID
*)&HandleTable
->TableCode
, (PVOID
)TableBase
);
493 else if (TableLevel
== 1)
495 /* Setup the 2nd level table */
496 SecondLevel
= (PVOID
)TableBase
;
498 /* Get if the next index can fit in the table */
499 i
= HandleTable
->NextHandleNeedingPool
/
500 INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES
);
501 if (i
< MID_LEVEL_ENTRIES
)
503 /* We need to allocate a new table */
504 Low
= ExpAllocateLowLevelTable(HandleTable
, DoInit
);
505 if (!Low
) return FALSE
;
507 /* Update the table */
508 Value
= InterlockedExchangePointer((PVOID
*)&SecondLevel
[i
], Low
);
509 ASSERT(Value
== NULL
);
513 /* We need a new high level table */
514 High
= ExpAllocateTablePagedPool(HandleTable
->QuotaProcess
,
515 SizeOfHandle(HIGH_LEVEL_ENTRIES
));
516 if (!High
) return FALSE
;
518 /* Allocate a new mid level table as well */
519 Mid
= ExpAllocateMidLevelTable(HandleTable
, DoInit
, &Low
);
522 /* We failed, free the high level table as well */
523 ExpFreeTablePagedPool(HandleTable
->QuotaProcess
,
525 SizeOfHandle(HIGH_LEVEL_ENTRIES
));
529 /* Link up the tables */
530 High
[0] = (PVOID
)TableBase
;
533 /* Write the new table and change the table code */
534 TableBase
= ((ULONG_PTR
)High
) | 2;
535 Value
= InterlockedExchangePointer((PVOID
*)&HandleTable
->TableCode
,
539 else if (TableLevel
== 2)
541 /* Setup the 3rd level table */
542 ThirdLevel
= (PVOID
)TableBase
;
544 /* Get the index and check if it can fit */
545 i
= HandleTable
->NextHandleNeedingPool
/ INDEX_TO_HANDLE_VALUE(MAX_MID_INDEX
);
546 if (i
>= HIGH_LEVEL_ENTRIES
) return FALSE
;
548 /* Check if there's no mid-level table */
551 /* Allocate a new mid level table */
552 Mid
= ExpAllocateMidLevelTable(HandleTable
, DoInit
, &Low
);
553 if (!Mid
) return FALSE
;
555 /* Update the table pointer */
556 Value
= InterlockedExchangePointer((PVOID
*)&ThirdLevel
[i
], Mid
);
557 ASSERT(Value
== NULL
);
561 /* We have one, check at which index we should insert our entry */
562 Index
= (HandleTable
->NextHandleNeedingPool
/ INDEX_TO_HANDLE_VALUE(1)) -
564 j
= Index
/ LOW_LEVEL_ENTRIES
;
566 /* Allocate a new low level */
567 Low
= ExpAllocateLowLevelTable(HandleTable
, DoInit
);
568 if (!Low
) return FALSE
;
570 /* Update the table pointer */
571 Value
= InterlockedExchangePointer((PVOID
*)&ThirdLevel
[i
][j
], Low
);
572 ASSERT(Value
== NULL
);
577 /* Something is really broken */
581 /* Update the index of the next handle */
582 Index
= InterlockedExchangeAdd((PLONG
) &HandleTable
->NextHandleNeedingPool
,
583 INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES
));
585 /* Check if need to initialize the table */
588 /* Create a new index number */
589 Index
+= INDEX_TO_HANDLE_VALUE(1);
591 /* Start free index change loop */
594 /* Setup the first free index */
595 FirstFree
= HandleTable
->FirstFree
;
596 Low
[LOW_LEVEL_ENTRIES
- 1].NextFreeTableEntry
= FirstFree
;
598 /* Change the index */
599 NewFree
= InterlockedCompareExchange((PLONG
) &HandleTable
->FirstFree
,
602 if (NewFree
== FirstFree
) break;
612 ExpMoveFreeHandles(IN PHANDLE_TABLE HandleTable
)
616 /* Clear the last free index */
617 LastFree
= InterlockedExchange((PLONG
) &HandleTable
->LastFree
, 0);
619 /* Check if we had no index */
620 if (!LastFree
) return LastFree
;
622 /* Acquire the locks we need */
623 for (i
= 1; i
< 4; i
++)
625 /* Acquire this lock exclusively */
626 ExWaitOnPushLock(&HandleTable
->HandleTableLock
[i
]);
629 /* Check if we're not strict FIFO */
630 if (!HandleTable
->StrictFIFO
)
632 /* Update the first free index */
633 if (!InterlockedCompareExchange((PLONG
) &HandleTable
->FirstFree
, LastFree
, 0))
635 /* We're done, exit */
640 /* We are strict FIFO, we need to reverse the entries */
647 ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable
,
648 OUT PEXHANDLE NewHandle
)
650 ULONG OldValue
, NewValue
, NewValue1
;
651 PHANDLE_TABLE_ENTRY Entry
;
652 EXHANDLE Handle
, OldHandle
;
656 /* Start allocation loop */
659 /* Get the current link */
660 OldValue
= HandleTable
->FirstFree
;
663 /* No free entries remain, lock the handle table */
664 KeEnterCriticalRegion();
665 ExAcquirePushLockExclusive(&HandleTable
->HandleTableLock
[0]);
667 /* Check the value again */
668 OldValue
= HandleTable
->FirstFree
;
671 /* Another thread has already created a new level, bail out */
672 ExReleasePushLockExclusive(&HandleTable
->HandleTableLock
[0]);
673 KeLeaveCriticalRegion();
677 /* Now move any free handles */
678 OldValue
= ExpMoveFreeHandles(HandleTable
);
681 /* Another thread has already moved them, bail out */
682 ExReleasePushLockExclusive(&HandleTable
->HandleTableLock
[0]);
683 KeLeaveCriticalRegion();
687 /* We're the first one through, so do the actual allocation */
688 Result
= ExpAllocateHandleTableEntrySlow(HandleTable
, TRUE
);
690 /* Unlock the table and get the value now */
691 ExReleasePushLockExclusive(&HandleTable
->HandleTableLock
[0]);
692 KeLeaveCriticalRegion();
693 OldValue
= HandleTable
->FirstFree
;
695 /* Check if allocation failed */
698 /* Check if nobody else went through here */
701 /* We're still the only thread around, so fail */
702 NewHandle
->GenericHandleOverlay
= NULL
;
708 /* We made it, write the current value */
709 Handle
.Value
= (OldValue
& FREE_HANDLE_MASK
);
711 /* Lookup the entry for this handle */
712 Entry
= ExpLookupHandleTableEntry(HandleTable
, Handle
);
714 /* Get an available lock and acquire it */
715 OldHandle
.Value
= OldValue
;
716 i
= OldHandle
.Index
% 4;
717 KeEnterCriticalRegion();
718 ExAcquirePushLockShared(&HandleTable
->HandleTableLock
[i
]);
720 /* Check if the value changed after acquiring the lock */
721 if (OldValue
!= *(volatile ULONG
*)&HandleTable
->FirstFree
)
723 /* It did, so try again */
724 ExReleasePushLockShared(&HandleTable
->HandleTableLock
[i
]);
725 KeLeaveCriticalRegion();
729 /* Now get the next value and do the compare */
730 NewValue
= *(volatile ULONG
*)&Entry
->NextFreeTableEntry
;
731 NewValue1
= InterlockedCompareExchange((PLONG
) &HandleTable
->FirstFree
,
735 /* The change was done, so release the lock */
736 ExReleasePushLockShared(&HandleTable
->HandleTableLock
[i
]);
737 KeLeaveCriticalRegion();
739 /* Check if the compare was successful */
740 if (NewValue1
== OldValue
)
742 /* Make sure that the new handle is in range, and break out */
743 ASSERT((NewValue
& FREE_HANDLE_MASK
) <
744 HandleTable
->NextHandleNeedingPool
);
749 /* The compare failed, make sure we expected it */
750 ASSERT((NewValue1
& FREE_HANDLE_MASK
) !=
751 (OldValue
& FREE_HANDLE_MASK
));
755 /* Increase the number of handles */
756 InterlockedIncrement(&HandleTable
->HandleCount
);
758 /* Return the handle and the entry */
765 ExCreateHandleTable(IN PEPROCESS Process OPTIONAL
)
767 PHANDLE_TABLE HandleTable
;
770 /* Allocate the handle table */
771 HandleTable
= ExpAllocateHandleTable(Process
, TRUE
);
772 if (!HandleTable
) return NULL
;
774 /* Acquire the handle table lock */
775 KeEnterCriticalRegion();
776 ExAcquirePushLockExclusive(&HandleTableListLock
);
778 /* Insert it into the list */
779 InsertTailList(&HandleTableListHead
, &HandleTable
->HandleTableList
);
781 /* Release the lock */
782 ExReleasePushLockExclusive(&HandleTableListLock
);
783 KeLeaveCriticalRegion();
785 /* Return the handle table */
791 ExCreateHandle(IN PHANDLE_TABLE HandleTable
,
792 IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
795 PHANDLE_TABLE_ENTRY NewEntry
;
798 /* Start with a clean handle */
799 Handle
.GenericHandleOverlay
= NULL
;
801 /* Allocate a new entry */
802 NewEntry
= ExpAllocateHandleTableEntry(HandleTable
, &Handle
);
805 /* Enter a critical region */
806 KeEnterCriticalRegion();
808 /* Write the entry */
809 *NewEntry
= *HandleTableEntry
;
811 /* Unlock it and leave the critical region */
812 ExUnlockHandleTableEntry(HandleTable
, NewEntry
);
813 KeLeaveCriticalRegion();
816 /* Return the handle value */
817 return Handle
.GenericHandleOverlay
;
822 ExpBlockOnLockedHandleEntry(IN PHANDLE_TABLE HandleTable
,
823 IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
826 EX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
828 /* Block on the pushlock */
829 ExBlockPushLock(&HandleTable
->HandleContentionEvent
, &WaitBlock
);
831 /* Get the current value and check if it's been unlocked */
832 OldValue
= HandleTableEntry
->Value
;
833 if (!(OldValue
) || (OldValue
& EXHANDLE_TABLE_ENTRY_LOCK_BIT
))
835 /* Unblock the pushlock and return */
836 ExfUnblockPushLock(&HandleTable
->HandleContentionEvent
, &WaitBlock
);
840 /* Wait for it to be unblocked */
841 ExWaitForUnblockPushLock(&HandleTable
->HandleContentionEvent
,
848 ExpLockHandleTableEntry(IN PHANDLE_TABLE HandleTable
,
849 IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
851 LONG_PTR NewValue
, OldValue
;
854 ASSERT((KeGetCurrentThread()->CombinedApcDisable
!= 0) ||
855 (KeGetCurrentIrql() == APC_LEVEL
));
857 /* Start lock loop */
860 /* Get the current value and check if it's locked */
861 OldValue
= *(volatile LONG_PTR
*)&HandleTableEntry
->Object
;
862 if (OldValue
& EXHANDLE_TABLE_ENTRY_LOCK_BIT
)
864 /* It's not locked, remove the lock bit to lock it */
865 NewValue
= OldValue
& ~EXHANDLE_TABLE_ENTRY_LOCK_BIT
;
866 if (InterlockedCompareExchangePointer(&HandleTableEntry
->Object
,
868 (PVOID
)OldValue
) == (PVOID
)OldValue
)
870 /* We locked it, get out */
876 /* We couldn't lock it, bail out if it's been freed */
877 if (!OldValue
) return FALSE
;
880 /* It's locked, wait for it to be unlocked */
881 ExpBlockOnLockedHandleEntry(HandleTable
, HandleTableEntry
);
887 ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable
,
888 IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
894 ASSERT((KeGetCurrentThread()->CombinedApcDisable
!= 0) ||
895 (KeGetCurrentIrql() == APC_LEVEL
));
897 /* Set the lock bit and make sure it wasn't earlier */
898 OldValue
= InterlockedOr((PLONG
) &HandleTableEntry
->Value
,
899 EXHANDLE_TABLE_ENTRY_LOCK_BIT
);
900 ASSERT((OldValue
& EXHANDLE_TABLE_ENTRY_LOCK_BIT
) == 0);
902 /* Unblock any waiters */
903 ExfUnblockPushLock(&HandleTable
->HandleContentionEvent
, NULL
);
908 ExRemoveHandleTable(IN PHANDLE_TABLE HandleTable
)
912 /* Acquire the table lock */
913 KeEnterCriticalRegion();
914 ExAcquirePushLockExclusive(&HandleTableListLock
);
916 /* Remove the table and reset the list */
917 RemoveEntryList(&HandleTable
->HandleTableList
);
918 InitializeListHead(&HandleTable
->HandleTableList
);
920 /* Release the lock */
921 ExReleasePushLockExclusive(&HandleTableListLock
);
922 KeLeaveCriticalRegion();
927 ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable
,
928 IN PVOID DestroyHandleProcedure OPTIONAL
)
932 /* Remove the handle from the list */
933 ExRemoveHandleTable(HandleTable
);
935 /* Check if we have a destroy callback */
936 if (DestroyHandleProcedure
)
942 /* Free the handle table */
943 ExpFreeHandleTable(HandleTable
);
948 ExDestroyHandle(IN PHANDLE_TABLE HandleTable
,
950 IN PHANDLE_TABLE_ENTRY HandleTableEntry OPTIONAL
)
956 /* Setup the actual handle value */
957 ExHandle
.GenericHandleOverlay
= Handle
;
959 /* Enter a critical region and check if we have to lookup the handle */
960 KeEnterCriticalRegion();
961 if (!HandleTableEntry
)
963 /* Lookup the entry */
964 HandleTableEntry
= ExpLookupHandleTableEntry(HandleTable
, ExHandle
);
966 /* Make sure that we found an entry, and that it's valid */
967 if (!(HandleTableEntry
) ||
968 !(HandleTableEntry
->Object
) ||
969 (HandleTableEntry
->NextFreeTableEntry
== -2))
971 /* Invalid handle, fail */
972 KeLeaveCriticalRegion();
977 if (!ExpLockHandleTableEntry(HandleTable
, HandleTableEntry
))
979 /* Couldn't lock, fail */
980 KeLeaveCriticalRegion();
986 /* Make sure the handle is locked */
987 ASSERT((HandleTableEntry
->Value
& EXHANDLE_TABLE_ENTRY_LOCK_BIT
) == 0);
990 /* Clear the handle */
991 Object
= InterlockedExchangePointer((PVOID
*)&HandleTableEntry
->Object
, NULL
);
994 ASSERT(Object
!= NULL
);
995 ASSERT((((ULONG_PTR
)Object
) & EXHANDLE_TABLE_ENTRY_LOCK_BIT
) == 0);
997 /* Unblock the pushlock */
998 ExfUnblockPushLock(&HandleTable
->HandleContentionEvent
, NULL
);
1000 /* Free the actual entry */
1001 ExpFreeHandleTableEntry(HandleTable
, ExHandle
, HandleTableEntry
);
1003 /* If we got here, return success */
1004 KeLeaveCriticalRegion();
1010 ExMapHandleToPointer(IN PHANDLE_TABLE HandleTable
,
1014 PHANDLE_TABLE_ENTRY HandleTableEntry
;
1017 /* Set the handle value */
1018 ExHandle
.GenericHandleOverlay
= Handle
;
1020 /* Fail if we got an invalid index */
1021 if (!(ExHandle
.Index
& (LOW_LEVEL_ENTRIES
- 1))) return NULL
;
1024 HandleTableEntry
= ExpLookupHandleTableEntry(HandleTable
, ExHandle
);
1025 if (!HandleTableEntry
) return NULL
;
1028 if (!ExpLockHandleTableEntry(HandleTable
, HandleTableEntry
)) return NULL
;
1030 /* Return the entry */
1031 return HandleTableEntry
;
1036 ExDupHandleTable(IN PEPROCESS Process
,
1037 IN PHANDLE_TABLE HandleTable
,
1038 IN PEX_DUPLICATE_HANDLE_CALLBACK DupHandleProcedure
,
1041 PHANDLE_TABLE NewTable
;
1043 PHANDLE_TABLE_ENTRY HandleTableEntry
, NewEntry
;
1044 BOOLEAN Failed
= FALSE
;
1047 /* Allocate the duplicated copy */
1048 NewTable
= ExpAllocateHandleTable(Process
, FALSE
);
1049 if (!NewTable
) return NULL
;
1051 /* Loop each entry */
1052 while (NewTable
->NextHandleNeedingPool
<
1053 HandleTable
->NextHandleNeedingPool
)
1055 /* Insert it into the duplicated copy */
1056 if (!ExpAllocateHandleTableEntrySlow(NewTable
, FALSE
))
1058 /* Insert failed, free the new copy and return */
1059 ExpFreeHandleTable(NewTable
);
1064 /* Setup the initial handle table data */
1065 NewTable
->HandleCount
= 0;
1066 NewTable
->ExtraInfoPages
= 0;
1067 NewTable
->FirstFree
= 0;
1069 /* Setup the first handle value */
1070 Handle
.Value
= INDEX_TO_HANDLE_VALUE(1);
1072 /* Enter a critical region and lookup the new entry */
1073 KeEnterCriticalRegion();
1074 while ((NewEntry
= ExpLookupHandleTableEntry(NewTable
, Handle
)))
1076 /* Lookup the old entry */
1077 HandleTableEntry
= ExpLookupHandleTableEntry(HandleTable
, Handle
);
1079 /* Loop each entry */
1082 /* Check if it doesn't match the audit mask */
1083 if (!(HandleTableEntry
->Value
& Mask
))
1085 /* Free it since we won't use it */
1090 /* Lock the entry */
1091 if (!ExpLockHandleTableEntry(HandleTable
, HandleTableEntry
))
1093 /* Free it since we can't lock it, so we won't use it */
1098 /* Copy the handle value */
1099 *NewEntry
= *HandleTableEntry
;
1101 /* Call the duplicate callback */
1102 if (DupHandleProcedure(Process
,
1107 /* Clear failure flag */
1110 /* Lock the entry, increase the handle count */
1111 NewEntry
->Value
|= EXHANDLE_TABLE_ENTRY_LOCK_BIT
;
1112 NewTable
->HandleCount
++;
1116 /* Duplication callback refused, fail */
1122 /* Check if we failed earlier and need to free */
1125 /* Free this entry */
1126 NewEntry
->Object
= NULL
;
1127 NewEntry
->NextFreeTableEntry
= NewTable
->FirstFree
;
1128 NewTable
->FirstFree
= (ULONG
)Handle
.Value
;
1131 /* Increase the handle value and move to the next entry */
1132 Handle
.Value
+= INDEX_TO_HANDLE_VALUE(1);
1135 } while (Handle
.Value
% INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES
));
1137 /* We're done, skip the last entry */
1138 Handle
.Value
+= INDEX_TO_HANDLE_VALUE(1);
1141 /* Acquire the table lock and insert this new table into the list */
1142 ExAcquirePushLockExclusive(&HandleTableListLock
);
1143 InsertTailList(&HandleTableListHead
, &NewTable
->HandleTableList
);
1144 ExReleasePushLockExclusive(&HandleTableListLock
);
1146 /* Leave the critical region we entered previously and return the table */
1147 KeLeaveCriticalRegion();
1153 ExChangeHandle(IN PHANDLE_TABLE HandleTable
,
1155 IN PEX_CHANGE_HANDLE_CALLBACK ChangeRoutine
,
1156 IN ULONG_PTR Context
)
1159 PHANDLE_TABLE_ENTRY HandleTableEntry
;
1160 BOOLEAN Result
= FALSE
;
1163 /* Set the handle value */
1164 ExHandle
.GenericHandleOverlay
= Handle
;
1166 /* Find the entry for this handle */
1167 HandleTableEntry
= ExpLookupHandleTableEntry(HandleTable
, ExHandle
);
1169 /* Make sure that we found an entry, and that it's valid */
1170 if (!(HandleTableEntry
) ||
1171 !(HandleTableEntry
->Object
) ||
1172 (HandleTableEntry
->NextFreeTableEntry
== -2))
1174 /* It isn't, fail */
1178 /* Enter a critical region */
1179 KeEnterCriticalRegion();
1181 /* Try locking the handle entry */
1182 if (ExpLockHandleTableEntry(HandleTable
, HandleTableEntry
))
1184 /* Call the change routine and unlock the entry */
1185 Result
= ChangeRoutine(HandleTableEntry
, Context
);
1186 ExUnlockHandleTableEntry(HandleTable
, HandleTableEntry
);
1189 /* Leave the critical region and return the callback result */
1190 KeLeaveCriticalRegion();
1196 ExSweepHandleTable(IN PHANDLE_TABLE HandleTable
,
1197 IN PEX_SWEEP_HANDLE_CALLBACK EnumHandleProcedure
,
1201 PHANDLE_TABLE_ENTRY HandleTableEntry
;
1204 /* Set the initial value and loop the entries */
1205 Handle
.Value
= INDEX_TO_HANDLE_VALUE(1);
1206 while ((HandleTableEntry
= ExpLookupHandleTableEntry(HandleTable
, Handle
)))
1208 /* Loop each handle */
1211 /* Lock the entry */
1212 if (ExpLockHandleTableEntry(HandleTable
, HandleTableEntry
))
1214 /* Notify the callback routine */
1215 EnumHandleProcedure(HandleTableEntry
,
1216 Handle
.GenericHandleOverlay
,
1220 /* Go to the next handle and entry */
1221 Handle
.Value
+= INDEX_TO_HANDLE_VALUE(1);
1223 } while (Handle
.Value
% INDEX_TO_HANDLE_VALUE(LOW_LEVEL_ENTRIES
));
1225 /* Skip past the last entry */
1226 Handle
.Value
+= INDEX_TO_HANDLE_VALUE(1);
1235 ExEnumHandleTable(IN PHANDLE_TABLE HandleTable
,
1236 IN PEX_ENUM_HANDLE_CALLBACK EnumHandleProcedure
,
1237 IN OUT PVOID Context
,
1238 OUT PHANDLE EnumHandle OPTIONAL
)
1241 PHANDLE_TABLE_ENTRY HandleTableEntry
;
1242 BOOLEAN Result
= FALSE
;
1245 /* Enter a critical region */
1246 KeEnterCriticalRegion();
1248 /* Set the initial value and loop the entries */
1250 while ((HandleTableEntry
= ExpLookupHandleTableEntry(HandleTable
, Handle
)))
1252 /* Validate the entry */
1253 if ((HandleTableEntry
->Object
) &&
1254 (HandleTableEntry
->NextFreeTableEntry
!= -2))
1256 /* Lock the entry */
1257 if (ExpLockHandleTableEntry(HandleTable
, HandleTableEntry
))
1259 /* Notify the callback routine */
1260 Result
= EnumHandleProcedure(HandleTableEntry
,
1261 Handle
.GenericHandleOverlay
,
1265 ExUnlockHandleTableEntry(HandleTable
, HandleTableEntry
);
1267 /* Was this the one looked for? */
1270 /* If so, return it if requested */
1271 if (EnumHandle
) *EnumHandle
= Handle
.GenericHandleOverlay
;
1277 /* Go to the next entry */
1278 Handle
.Value
+= INDEX_TO_HANDLE_VALUE(1);
1281 /* Leave the critical region and return callback result */
1282 KeLeaveCriticalRegion();