diff options
Diffstat (limited to 'private/ntos/ex/handle.c')
-rw-r--r-- | private/ntos/ex/handle.c | 1520 |
1 files changed, 1520 insertions, 0 deletions
diff --git a/private/ntos/ex/handle.c b/private/ntos/ex/handle.c new file mode 100644 index 000000000..53fb66944 --- /dev/null +++ b/private/ntos/ex/handle.c @@ -0,0 +1,1520 @@ +/*++ + +Copyright (c) 1989-1995 Microsoft Corporation + +Module Name: + + handle.c + +Abstract: + + This module implements a set of functions for supporting handles. + +Author: + + Steve Wood (stevewo) 25-Apr-1989 + David N. Cutler (davec) 17-May-1995 (rewrite) + +Revision History: + +--*/ + +#include "exp.h" +#pragma hdrstop + +// +// Define global values for the default initial handle entry table size and +// the default size to grow the handle entry table. +// + +USHORT ExpDefaultHandleTableSize; +USHORT ExpDefaultHandleTableGrowth; + +// +// Decline global structures that link all handle tables together. +// + +ERESOURCE HandleTableListLock; +LIST_ENTRY HandleTableListHead; + +// +// Define forward referenced prototypes. +// + +PHANDLE_TABLE +ExpAllocateHandleTable( + IN PEPROCESS Process, + IN ULONG CountToGrowBy + ); + +PHANDLE_ENTRY +ExpAllocateHandleTableEntries( + IN PHANDLE_TABLE HandleTable, + IN PVOID OldTableEntries, + IN ULONG OldCountEntries, + IN ULONG NewCountEntries + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, ExInitializeHandleTablePackage) +#pragma alloc_text(PAGE, ExChangeHandle) +#pragma alloc_text(PAGE, ExCreateHandle) +#pragma alloc_text(PAGE, ExCreateHandleTable) +#pragma alloc_text(PAGE, ExDestroyHandle) +#pragma alloc_text(PAGE, ExDestroyHandleTable) +#pragma alloc_text(PAGE, ExDupHandleTable) +#pragma alloc_text(PAGE, ExEnumHandleTable) +#pragma alloc_text(PAGE, ExMapHandleToPointer) +#pragma alloc_text(PAGE, ExRemoveHandleTable) +#pragma alloc_text(PAGE, ExSnapShotHandleTables) +#pragma alloc_text(PAGE, ExpAllocateHandleTable) +#pragma alloc_text(PAGE, ExpAllocateHandleTableEntries) +#endif + +VOID +ExInitializeHandleTablePackage( + VOID + ) + +/*++ + +Routine Description: + + This function initializes static data structures required to support + handle table. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + MM_SYSTEMSIZE SystemSize; + + // + // Get the configuration size of the host system and set the initial + // and growth default sizes for handle table as appropriate. + // + // N.B. The initial sizes are set such that otimal use of pool block + // storage can be obtained. + // + + SystemSize = MmQuerySystemSize(); + if (SystemSize == MmSmallSystem) { + ExpDefaultHandleTableSize = 7; + ExpDefaultHandleTableGrowth = 8; + + } else { + ExpDefaultHandleTableSize = 15; + ExpDefaultHandleTableGrowth = 16; + } + + // + // Initialize the handle table synchronization resource and listhead. + // + + InitializeListHead(&HandleTableListHead); + ExInitializeResource(&HandleTableListLock); + return; +} + +VOID +FASTCALL +ExAcquireHandleTableExclusive( + IN PHANDLE_TABLE HandleTable + ) + +/*++ + +Routine Description: + + This routine acquires the specified handle table for exclusive access. + + N.B. This routine uses fast locking. + +Arguments: + + HandleTable - Supplies a pointer to the handle table that is acquired + for exclusive access. + +Return Value: + + None. + +--*/ + +{ + + HANDLE_SYNCH Current; + HANDLE_SYNCH NewState; + + ASSERT(KeIsExecutingDpc() == FALSE); + + do { + + // + // Capture the current state of handle table ownership and initialize + // the proposed new state value. + // + // If the handle table is not owned, then attempt to grant exclusive + // ownership. Otherwise, the handle table is owned either shared or + // exclusive and the calling thread must wait for exclusive access. + // + + NewState.Value = Current.Value = *((volatile ULONGLONG *)&HandleTable->State.Value); + if (Current.u.OwnerCount == 0) { + NewState.u.OwnerCount = (ULONG)PsGetCurrentThread(); + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + break; + } + + } else { + + ASSERT((PETHREAD)(HandleTable->State.u.OwnerCount) != PsGetCurrentThread()); + + NewState.u.NumberOfExclusiveWaiters += 1; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + KeWaitForSingleObject(&HandleTable->ExclusiveWaiters, + Executive, + KernelMode, + FALSE, + NULL); + break; + } + } + + } while (TRUE); + + return; +} + +VOID +FASTCALL +ExAcquireHandleTableShared( + IN PHANDLE_TABLE HandleTable + ) + +/*++ + +Routine Description: + + This routine acquires the specified handle table for shared access. + + N.B. This routines uses fast locking. + +Arguments: + + HandleTable - Supplies a pointer to the handle table that is acquired + for shared access. + +Return Value: + + None. + +--*/ + +{ + + HANDLE_SYNCH Current; + HANDLE_SYNCH NewState; + + ASSERT(KeIsExecutingDpc() == FALSE); + + do { + + // + // Capture the current state of handle table ownership and initialize + // the proposed new state value. + // + // If the handle table is not owned or is owned shared, then attempt + // to grant shared ownership. Otherwise, the handle table is owned + // exclusive and the calling thread must wait for shared access. + // + // N.B. Shared access is granted if shared access is already granted + // regardless of exclusive waiters. + // + + NewState.Value = Current.Value = *((volatile ULONGLONG *)&HandleTable->State.Value); + if ((Current.u.OwnerCount == 0) || + ((Current.u.OwnerCount & 1) != 0)) { + NewState.u.OwnerCount = (NewState.u.OwnerCount + 2) | 1; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + break; + } + + } else { + NewState.u.NumberOfSharedWaiters += 1; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + KeWaitForSingleObject(&HandleTable->SharedWaiters, + Executive, + KernelMode, + FALSE, + NULL); + break; + } + } + + } while (TRUE); + + return; +} + +VOID +FASTCALL +ExReleaseHandleTableExclusive( + IN PHANDLE_TABLE HandleTable + ) + +/*++ + +Routine Description: + + This routine releases exclusive access to the specified handle table + for the current thread. + + N.B. This routine uses fast locking. + +Arguments: + + HandleTable - Supplies a pointer to the handle table to release. + +Return Value: + + None. + +--*/ + +{ + + HANDLE_SYNCH Current; + HANDLE_SYNCH NewState; + ULONG Number; + + ASSERT(KeIsExecutingDpc() == FALSE); + + ASSERT(((PETHREAD)HandleTable->State.u.OwnerCount == PsGetCurrentThread()) || + (HandleTable->State.u.OwnerCount == 2)); + + do { + + // + // Capture the current state of handle table ownership and initialize + // the proposed new state value. + // + // If there are shared waiters, then attempt to grant shared access to + // the handle table. Otherwise, it there are exclusive waiters, then + // attempt to grant exclusive access to the handle table. Otherwise, + // clear ownership of the handle table. + // + + NewState.Value = Current.Value = *((volatile ULONGLONG *)&HandleTable->State.Value); + if ((Number = Current.u.NumberOfSharedWaiters) != 0) { + NewState.u.NumberOfSharedWaiters = 0; + NewState.u.OwnerCount = (Number * 2) | 1; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + KeReleaseSemaphore(&HandleTable->SharedWaiters, 0, Number, FALSE); + break; + } + + } else if (Current.u.NumberOfExclusiveWaiters != 0) { + NewState.u.OwnerCount = 2; + NewState.u.NumberOfExclusiveWaiters -= 1; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + KeSetEventBoostPriority(&HandleTable->ExclusiveWaiters, + (PRKTHREAD *)&HandleTable->State.u.OwnerCount); + + break; + } + + } else { + NewState.u.OwnerCount = 0; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + break; + } + } + + } while (TRUE); + + return; +} + +VOID +FASTCALL +ExReleaseHandleTableShared( + IN PHANDLE_TABLE HandleTable + ) + +/*++ + +Routine Description: + + This routine releases shared access to the specified handle table for + the current thread. + + N.B. This routine uses fast locking. + +Arguments: + + HandleTable - Supplies a pointer to the handle table to release. + +Return Value: + + None. + +--*/ + +{ + + HANDLE_SYNCH Current; + HANDLE_SYNCH NewState; + ULONG Number; + + ASSERT(KeIsExecutingDpc() == FALSE); + + ASSERT((HandleTable->State.u.OwnerCount & 1) == 1); + + do { + + // + // Capture the current state of handle table ownership and initialize + // the proposed new state value. + // + // If there are exclusive waiters, then attempt to grant exclusive + // access to the handle table. Otherwise, clear ownership of the + // handle table (it is not possible to have shared waiters). + // + + NewState.Value = Current.Value = *((volatile ULONGLONG *)&HandleTable->State.Value); + if (Current.u.OwnerCount != 3) { + NewState.u.OwnerCount -= 2; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + break; + } + + } else if (Current.u.NumberOfExclusiveWaiters != 0) { + NewState.u.OwnerCount = 2; + NewState.u.NumberOfExclusiveWaiters -= 1; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + KeSetEventBoostPriority(&HandleTable->ExclusiveWaiters, + (PRKTHREAD *)&HandleTable->State.u.OwnerCount); + + break; + } + + } else { + NewState.u.OwnerCount = 0; + if (ExInterlockedCompareExchange64(&HandleTable->State.Value, + &NewState.Value, + &Current.Value, + &HandleTable->SpinLock) == Current.Value) { + + break; + } + } + + } while (TRUE); + + return; +} + +BOOLEAN +ExChangeHandle( + IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN PEX_CHANGE_HANDLE_ROUTINE ChangeRoutine, + IN ULONG Parameter + ) + +/*++ + +Routine Description: + + This function provides the capability to change the contents of the + handle entry corrsponding to the specified handle. + +Arguments: + + HandleTable - Supplies a pointer to a handle table. + + Handle - Supplies the handle for the handle entry that is changed. + + ChangeRoutine - Supplies a pointer to a function that is called to + perform the change. + + Parameter - Supplies an uninterpreted parameter that is passed to + the change routine. + +Return Value: + + If the operation was successfully performed, then a value of TRUE + is returned. Otherwise, a value of FALSE is returned. + +--*/ + +{ + + PHANDLE_ENTRY HandleEntry; + BOOLEAN ReturnValue; + PHANDLE_ENTRY TableBound; + PHANDLE_ENTRY TableEntries; + ULONG TableIndex; + + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + + // + // Lock the handle table exclusive and check if the handle is valid. + // + + ReturnValue = FALSE; + TableIndex = HANDLE_TO_INDEX(Handle); + ExLockHandleTableExclusive(HandleTable); + TableBound = HandleTable->TableBound; + TableEntries = HandleTable->TableEntries; + if (TableIndex < (ULONG)(TableBound - TableEntries)) { + + // + // Compute the address of the handle entry and call the change + // handle function if the handle entry is not free. + // + + HandleEntry = &TableEntries[TableIndex]; + if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) { + ReturnValue = (*ChangeRoutine)(HandleEntry, Parameter); + } + } + + ExUnlockHandleTableExclusive(HandleTable); + return ReturnValue; +} + +HANDLE +ExCreateHandle( + IN PHANDLE_TABLE HandleTable, + IN PHANDLE_ENTRY HandleEntry + ) + +/*++ + +Routine Description: + + This function create a handle entry in the specified handle table and + returns a handle for the entry. If there is insufficient room in the + handle table for a new entry, then the handle table is expanded if + possible. + +Arguments: + + HandleTable - Supplies a pointer to a handle table + + HandleEntry - Supplies a poiner to the handle entry for which a + handle entry is created. + +Return Value: + + If the handle entry is successfully created, then value of the created + handle is returned as the function value. Otherwise, a value of NULL is + returned. + +--*/ + +{ + + PLIST_ENTRY FreeEntry; + ULONG NewCountEntries; + PHANDLE_ENTRY NewEntry; + ULONG OldCountEntries; + PHANDLE_ENTRY TableEntries; + ULONG TableIndex; + + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + ASSERT(HandleEntry != NULL); + + // + // Lock the handle table exclusive and allocate a free handle entry. + // + + ExLockHandleTableExclusive(HandleTable); + TableEntries = HandleTable->TableEntries; + if (IsListEmpty(&TableEntries->ListEntry)) { + + // + // There are no free entries in the handle entry table. Attempt + // to grow the size of the handle entry table. + // + + OldCountEntries = HandleTable->TableBound - TableEntries; + NewCountEntries = OldCountEntries + HandleTable->CountToGrowBy; + if (ExpAllocateHandleTableEntries(HandleTable, + TableEntries, + OldCountEntries, + NewCountEntries) == NULL) { + + ExUnlockHandleTableExclusive(HandleTable); + return NULL; + + } else { + TableEntries = HandleTable->TableEntries; + } + } + + // + // Remove the first entry from the free list, initialize the handle + // entry, unlock the handle table, and return the handle value. + // + // N.B. The LIFO/FIFO discipline for handle table entires is maintained + // at the point handles are destroyed. + // + + FreeEntry = TableEntries->ListEntry.Flink; + RemoveEntryList(FreeEntry); + NewEntry = CONTAINING_RECORD(FreeEntry, HANDLE_ENTRY, ListEntry); + HandleTable->HandleCount += 1; + NewEntry->Object = HandleEntry->Object; + NewEntry->Attributes = HandleEntry->Attributes; + ExUnlockHandleTableExclusive(HandleTable); + TableIndex = NewEntry - TableEntries; + return INDEX_TO_HANDLE(TableIndex); +} + +PHANDLE_TABLE +ExCreateHandleTable( + IN PEPROCESS Process OPTIONAL, + IN ULONG CountEntries, + IN ULONG CountToGrowBy + ) + +/*++ + +Routine Description: + + This function creates a handle table and allocates the specified count + of initial handle entries. + +Arguments: + + Process - Supplies an optional pointer to the process against which quota + will be charged. + + CountEntries - Supplies the initial number of handle entries to allocate. + + CountToGrowBy - Supplies the number of handle entries to grow the handle + entry table by when it becomes full. + +Return Value: + + If a handle table is successfully created, then the address of the + handle table is returned as the function value. Otherwize, a value + NULL is returned. + +--*/ + +{ + + PHANDLE_TABLE HandleTable; + + PAGED_CODE(); + + // + // If the number of initial handle entries or the number to grow by + // are not specified, then use the system default value. + // + + if ((CountEntries <= 1) || (CountEntries > MAXUSHORT)) { + CountEntries = ExpDefaultHandleTableSize; + } + + if ((CountToGrowBy <= 1) || (CountToGrowBy > MAXUSHORT)) { + CountToGrowBy = ExpDefaultHandleTableGrowth; + } + + // + // Allocate and initialize a handle table descriptor. + // + + HandleTable = ExpAllocateHandleTable(Process, CountToGrowBy); + + // + // If the handle table descriptor was successfully allocated, then + // allocate the initial handle entry table. + // + + if (HandleTable != NULL) { + if (ExpAllocateHandleTableEntries(HandleTable, + NULL, + 0, + CountEntries) == NULL) { + + ExDestroyHandleTable(HandleTable, NULL); + HandleTable = NULL; + } + } + + return HandleTable; +} + +BOOLEAN +ExDestroyHandle( + IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN BOOLEAN TableLocked + ) + +/*++ + +Routine Description: + + This function removes a handle from a handle table. + +Arguments: + + HandleTable - Supplies a pointer to a handle table + + Handle - Supplies the handle value of the entry to remove. + + TableLocked - Supplies a boolean value that determines whether the + handle table is already locked. + +Return Value: + + If the specified handle is successfully removed, then a value of + TRUE is returned. Otherwise, a value of FALSE is returned. + +--*/ + +{ + + PHANDLE_ENTRY HandleEntry; + BOOLEAN ResultValue; + PHANDLE_ENTRY TableBound; + PHANDLE_ENTRY TableEntries; + ULONG TableIndex; + + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + + // + // If the handle table is not already locked, then lock the handle + // table exclussive. + // + + ResultValue = FALSE; + TableIndex = HANDLE_TO_INDEX(Handle); + if (TableLocked == FALSE) { + ExLockHandleTableExclusive(HandleTable); + } + + TableBound = HandleTable->TableBound; + TableEntries = HandleTable->TableEntries; + if (TableIndex < (ULONG)(TableBound - TableEntries)) { + + // + // Compute the address of the handle entry and check if the handle + // is is use. + // + + HandleEntry = &TableEntries[TableIndex]; + if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) { + + // + // Insert the handle entry in the free list according to the + // discipline associated with the handle table and decrement + // the number of handles. + // + + HandleTable->HandleCount -= 1; + if (HandleTable->LifoOrder != FALSE) { + InsertHeadList(&TableEntries->ListEntry, &HandleEntry->ListEntry); + + } else { + InsertTailList(&TableEntries->ListEntry, &HandleEntry->ListEntry); + } + + ResultValue = TRUE; + } + } + + // + // Unlock the handle table if is was locked. + // + + if (TableLocked == FALSE) { + ExUnlockHandleTableExclusive(HandleTable); + } + + return ResultValue; +} + +VOID +ExRemoveHandleTable( + IN PHANDLE_TABLE HandleTable + ) + +/*++ + +Routine Description: + + This function removes the specified handle table from the list of + handle tables. Used by PS and ATOM packages to make sure their + handle tables are not in the list enumerated by the ExSnapShotHandleTables + routine and the !handle debugger extension. + +Arguments: + + HandleTable - Supplies a pointer to a handle table + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + + // + // Remove the handle table from the handle table list. + // + + KeEnterCriticalRegion(); + ExAcquireResourceExclusive(&HandleTableListLock, TRUE); + if (!IsListEmpty(&HandleTable->ListEntry)) { + RemoveEntryList(&HandleTable->ListEntry); + InitializeListHead(&HandleTable->ListEntry); + } + ExReleaseResource(&HandleTableListLock); + KeLeaveCriticalRegion(); + return; +} + +VOID +ExDestroyHandleTable( + IN PHANDLE_TABLE HandleTable, + IN EX_DESTROY_HANDLE_ROUTINE DestroyHandleProcedure OPTIONAL + ) + +/*++ + +Routine Description: + + This function destroys the specified handle table. + +Arguments: + + HandleTable - Supplies a pointer to a handle table + + DestroyHandleProcedure - Supplies a pointer to a function to call for + each valid handle entry in the handle table. + +Return Value: + + None. + +--*/ + +{ + + ULONG CountEntries; + PHANDLE_ENTRY HandleEntry; + PEPROCESS Process; + PHANDLE_ENTRY TableBound; + PHANDLE_ENTRY TableEntries; + ULONG TableIndex; + + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + + // + // Remove the handle table from the handle table list. + // + + ExRemoveHandleTable(HandleTable); + + // + // If a handle entry table has been allocated, then scan all of + // handle entries, call the destroy handle function, if specfied, + // free the allocated pool, and return pool quota as appropriate. + // + + Process = HandleTable->QuotaProcess; + TableBound = HandleTable->TableBound; + TableEntries = HandleTable->TableEntries; + if (TableEntries != NULL) { + if (ARGUMENT_PRESENT(DestroyHandleProcedure)) { + HandleEntry = &TableEntries[1]; + while (HandleEntry < TableBound) { + if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) { + TableIndex = HandleEntry - TableEntries; + (*DestroyHandleProcedure)(INDEX_TO_HANDLE(TableIndex), + HandleEntry); + } + + HandleEntry += 1; + } + } + + ExFreePool(TableEntries); + if (Process != NULL) { + CountEntries = TableBound - TableEntries; + PsReturnPoolQuota(Process, + PagedPool, + CountEntries * sizeof(HANDLE_ENTRY)); + } + } + + // + // Free the allocated pool and return pool quota as appropriate. + // + + ExFreePool(HandleTable); + if (Process != NULL) { + PsReturnPoolQuota(Process, + NonPagedPool, + sizeof(HANDLE_TABLE)); + + } + + return; +} + +PHANDLE_TABLE +ExDupHandleTable( + IN PEPROCESS Process OPTIONAL, + IN PHANDLE_TABLE OldHandleTable, + IN EX_DUPLICATE_HANDLE_ROUTINE DupHandleProcedure OPTIONAL + ) + +/*++ + +Routine Description: + + This function creates a duplicate copy of the specified handle table. + +Arguments: + + Process - Supplies an optional to the process to charge quota to. + + OldHandleTable - Supplies a pointer to a handle table. + + DupHandleProcedure - Supplies an optional pointer to a function to call + for each valid handle in the duplicated handle table. + +Return Value: + + If the specified handle table is successfully duplicated, then the + address of the new handle table is returned as the function value. + Otherwize, a value NULL is returned. + +--*/ + +{ + + PLIST_ENTRY FreeHead; + PHANDLE_TABLE NewHandleTable; + PHANDLE_ENTRY NewHandleEntry; + PHANDLE_ENTRY NewTableEntries; + PHANDLE_ENTRY OldHandleEntry; + ULONG OldCountEntries; + PHANDLE_ENTRY OldTableBound; + PHANDLE_ENTRY OldTableEntries; + + PAGED_CODE(); + + ASSERT(OldHandleTable != NULL); + + // + // Lock the old handle table exclusive and allocate and initialize + // a handle table descriptor. + // + + ExLockHandleTableExclusive(OldHandleTable); + NewHandleTable = ExpAllocateHandleTable(Process, + OldHandleTable->CountToGrowBy); + + // + // If the new handle table descriptor was successfully allocated, then + // the allocate the handle entry table. + // + + if (NewHandleTable != NULL) { + OldTableBound = OldHandleTable->TableBound; + OldTableEntries = OldHandleTable->TableEntries; + OldCountEntries = OldTableBound - OldTableEntries; + if (ExpAllocateHandleTableEntries(NewHandleTable, + OldTableEntries, + 0, + OldCountEntries) == NULL) { + + ExDestroyHandleTable(NewHandleTable, NULL); + NewHandleTable = NULL; + + } else { + + // + // Scan through the old handle table and either duplicate the + // associated entry or insert it in the free list. + // + + NewTableEntries = NewHandleTable->TableEntries; + FreeHead = &NewTableEntries->ListEntry; + OldHandleEntry = &OldTableEntries[1]; + NewHandleEntry = &NewTableEntries[1]; + while (OldHandleEntry < OldTableBound) { + if (ExIsEntryFree(OldTableEntries, OldTableBound, OldHandleEntry)) { + InsertTailList(FreeHead, &NewHandleEntry->ListEntry); + + } else { + NewHandleEntry->Object = OldHandleEntry->Object; + NewHandleEntry->Attributes = OldHandleEntry->Attributes; + if (ARGUMENT_PRESENT(DupHandleProcedure)) { + if ((*DupHandleProcedure)(Process, NewHandleEntry)) { + NewHandleTable->HandleCount += 1; + + } else { + InsertTailList(FreeHead, &NewHandleEntry->ListEntry); + } + + } else { + NewHandleTable->HandleCount += 1; + } + } + + NewHandleEntry += 1; + OldHandleEntry += 1; + } + } + } + + ExUnlockHandleTableExclusive(OldHandleTable); + return NewHandleTable; +} + +BOOLEAN +ExEnumHandleTable( + IN PHANDLE_TABLE HandleTable, + IN EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure, + IN PVOID EnumParameter, + OUT PHANDLE Handle OPTIONAL + ) + +/*++ + +Routine Description: + + This function enumerates all the valid handles in a handle table. + For each valid handle in the handle table, the specified eumeration + function is called. If the enumeration function returns TRUE, then + the enumeration is stopped, the current handle is returned to the + caller via the optional Handle parameter, and this function returns + TRUE to indicated that the enumeration stopped at a specific handle. + +Arguments: + + HandleTable - Supplies a pointer to a handle table. + + EnumHandleProcedure - Supplies a pointer to a fucntion to call for + each valid handle in the enumerated handle table. + + EnumParameter - Supplies an uninterpreted 32-bit value that is passed + to the EnumHandleProcedure each time it is called. + + Handle - Supplies an optional pointer a variable that receives the + Handle value that the enumeration stopped at. Contents of the + variable only valid if this function returns TRUE. + +Return Value: + + If the enumeration stopped at a specific handle, then a value of TRUE + is returned. Otherwise, a value of FALSE is returned. + +--*/ + +{ + + PHANDLE_ENTRY HandleEntry; + BOOLEAN ResultValue; + PHANDLE_ENTRY TableEntries; + PHANDLE_ENTRY TableBound; + ULONG TableIndex; + + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + + // + // Lock the handle table exclusive and enumerate the handle entries. + // + + ResultValue = FALSE; + ExLockHandleTableShared(HandleTable); + TableBound = HandleTable->TableBound; + TableEntries = HandleTable->TableEntries; + HandleEntry = &TableEntries[1]; + while (HandleEntry < TableBound) { + if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) { + TableIndex = HandleEntry - TableEntries; + if ((*EnumHandleProcedure)(HandleEntry, + INDEX_TO_HANDLE(TableIndex), + EnumParameter)) { + + if (ARGUMENT_PRESENT(Handle)) { + *Handle = INDEX_TO_HANDLE(TableIndex); + } + + ResultValue = TRUE; + break; + } + } + + HandleEntry += 1; + } + + ExUnlockHandleTableShared(HandleTable); + return ResultValue; +} + +PHANDLE_ENTRY +ExMapHandleToPointer( + IN PHANDLE_TABLE HandleTable, + IN HANDLE Handle, + IN BOOLEAN Shared + ) + +/*++ + +Routine Description: + + This function maps a handle to a pointer to a handle entry. If the + map operation is successful, then this function returns with the + handle table locked. + +Arguments: + + HandleTable - Supplies a pointer to a handle table. + + Handle - Supplies the handle to be mapped to a handle entry. + + Shared - Supplies a boolean value that determines whether the handle + table is locked for shared (TRUE) or exclusive (FALSE) access. + +Return Value: + + If the handle was successfully mapped to a pointer to a handle entry, + then the address of the handle entry is returned as the function value + with the handle table locked. Otherwise, a value of NULL is returned with + the handle table unlocked. + +--*/ + +{ + + PHANDLE_ENTRY HandleEntry; + PHANDLE_ENTRY TableBound; + PHANDLE_ENTRY TableEntries; + ULONG TableIndex; + + PAGED_CODE(); + + ASSERT(HandleTable != NULL); + + // + // Lock the handle table exclusive or shared and check if the handle + // if valid. + // + + TableIndex = HANDLE_TO_INDEX(Handle); + if (Shared != FALSE) { + ExLockHandleTableShared(HandleTable); + + } else { + ExLockHandleTableExclusive(HandleTable); + } + + TableBound = HandleTable->TableBound; + TableEntries = HandleTable->TableEntries; + if (TableIndex < (ULONG)(TableBound - TableEntries)) { + HandleEntry = &TableEntries[TableIndex]; + if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) { + return HandleEntry; + } + } + + if (Shared != FALSE) { + ExUnlockHandleTableShared(HandleTable); + + } else { + ExUnlockHandleTableExclusive(HandleTable); + } + + return NULL; +} + +NTSTATUS +ExSnapShotHandleTables( + IN PEX_SNAPSHOT_HANDLE_ENTRY SnapShotHandleEntry, + IN OUT PSYSTEM_HANDLE_INFORMATION HandleInformation, + IN ULONG Length, + IN OUT PULONG RequiredLength + ) + +/*++ + +Routine Description: + + This function sets the handle allocation algorithm for the specified + handle table. + +Arguments: + + ... + +Return Value: + + None. + +--*/ + +{ + + PHANDLE_ENTRY HandleEntry; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO HandleEntryInfo; + PHANDLE_TABLE HandleTable; + PLIST_ENTRY NextEntry; + NTSTATUS Status; + PHANDLE_ENTRY TableBound; + PHANDLE_ENTRY TableEntries; + ULONG TableIndex; + + PAGED_CODE(); + + // + // Lock the handle table list exclusive and traverse the list of + // handle tables. + // + + KeEnterCriticalRegion(); + ExAcquireResourceExclusive(&HandleTableListLock, TRUE); + try { + HandleEntryInfo = &HandleInformation->Handles[0]; + NextEntry = HandleTableListHead.Flink; + while (NextEntry != &HandleTableListHead) { + + // + // Get the address of the next handle table, lock the handle + // table exclusive, and scan the list of handle entries. + // + + HandleTable = CONTAINING_RECORD(NextEntry, HANDLE_TABLE, ListEntry); + ExLockHandleTableExclusive(HandleTable); + TableBound = HandleTable->TableBound; + TableEntries = HandleTable->TableEntries; + HandleEntry = &TableEntries[1]; + try { + for (HandleEntry = &TableEntries[1]; + HandleEntry < TableBound; + HandleEntry++) { + if (ExIsEntryUsed(TableEntries, TableBound, HandleEntry)) { + HandleInformation->NumberOfHandles += 1; + TableIndex = HandleEntry - TableEntries; + Status = (*SnapShotHandleEntry)(&HandleEntryInfo, + HandleTable->UniqueProcessId, + HandleEntry, + INDEX_TO_HANDLE(TableIndex), + Length, + RequiredLength); + } + } + + } finally { + ExUnlockHandleTableExclusive(HandleTable); + } + + NextEntry = NextEntry->Flink; + } + + } finally { + ExReleaseResource(&HandleTableListLock); + KeLeaveCriticalRegion(); + } + + return Status; +} + +PHANDLE_TABLE +ExpAllocateHandleTable( + IN PEPROCESS Process OPTIONAL, + IN ULONG CountToGrowBy + ) + +/*++ + +Routine Description: + + This function allocates and initializes a new handle table descriptor. + +Arguments: + + Process - Supplies an optional pointer to a process to charge quota + against. + + CountToGrowBy - Supplies the number of handle entries to grow the + handle table by. + +Return Value: + + If a handle is successfully allocated, then the address of the handle + table is returned as the function value. Otherwise, NULL is returned. + +--*/ + +{ + + PHANDLE_TABLE HandleTable; + + PAGED_CODE(); + + // + // Allocate handle table from nonpaged pool. + // + + HandleTable = + (PHANDLE_TABLE)ExAllocatePoolWithTag(NonPagedPool, + sizeof(HANDLE_TABLE), + 'btbO'); + + // + // If the allocation is successful, then attempt to charge quota as + // appropriate and initialize the handle tabel descriptor. + // + + if (HandleTable != NULL) { + if (ARGUMENT_PRESENT(Process)) { + try { + PsChargePoolQuota(Process, + NonPagedPool, + sizeof(HANDLE_TABLE)); + + } except (EXCEPTION_EXECUTE_HANDLER) { + ExFreePool(HandleTable); + return NULL; + } + } + + // + // Initialize the handle table access synchronization information. + // + + HandleTable->State.u.OwnerCount = 0; + HandleTable->State.u.NumberOfSharedWaiters = 0; + HandleTable->State.u.NumberOfExclusiveWaiters = 0; + KeInitializeSpinLock(&HandleTable->SpinLock); + KeInitializeSemaphore(&HandleTable->SharedWaiters, 0, MAXLONG); + KeInitializeEvent(&HandleTable->ExclusiveWaiters, + SynchronizationEvent, + FALSE); + + // + // Initialize the handle table descriptor. + // + + HandleTable->LifoOrder = FALSE; + HandleTable->UniqueProcessId = PsGetCurrentProcess()->UniqueProcessId; + HandleTable->TableEntries = NULL; + HandleTable->TableBound = NULL; + HandleTable->QuotaProcess = Process; + HandleTable->HandleCount = 0; + HandleTable->CountToGrowBy = (USHORT)CountToGrowBy; + + // + // Insert the handle table in the handle table list. + // + + KeEnterCriticalRegion(); + ExAcquireResourceExclusive(&HandleTableListLock, TRUE); + InsertTailList(&HandleTableListHead, &HandleTable->ListEntry); + ExReleaseResource(&HandleTableListLock); + KeLeaveCriticalRegion(); + } + + return HandleTable; +} + +PHANDLE_ENTRY +ExpAllocateHandleTableEntries( + IN PHANDLE_TABLE HandleTable, + IN PHANDLE_ENTRY OldTableEntries, + IN ULONG OldCountEntries, + IN ULONG NewCountEntries + ) + +/*++ + +Routine Description: + + This function allocates and initializes a new set of free handle + table entries. + +Arguments: + + HandleTable - Supplies a pointer to a handle table descriptor. + + OldTableEntries - Supplies a pointer to the old set of handle table + entries. + + OldCountEntries - Supplies the previous count of table entries. + + NewCountEntries - Supplies the desired count of table entries. + +Return Value: + + If a new set of handle entries is successfully allocated, then the + address of the handle entries is returned as the function value. + Otherwise, NULL is returned. + +--*/ + +{ + + PHANDLE_ENTRY FreeEntry; + PLIST_ENTRY FreeHead; + ULONG NewCountBytes; + PHANDLE_ENTRY NewTableBound; + PHANDLE_ENTRY NewTableEntries; + LONG OldCountBytes; + PEPROCESS Process; + BOOLEAN Status; + + PAGED_CODE(); + + ASSERT(NewCountEntries > OldCountEntries); + + // + // Compute the old and new sizes for the handle entry table and + // allocate a new handle entry table. + // + + OldCountBytes = OldCountEntries * sizeof(HANDLE_ENTRY); + NewCountBytes = NewCountEntries * sizeof(HANDLE_ENTRY); + NewTableEntries = (PHANDLE_ENTRY)ExAllocatePoolWithTag(PagedPool, + NewCountBytes, + 'btbO'); + + // + // If the allocation is successful, then attempt to charge quota as + // appropriate and initialize the free list of handle entries. + // + + if (NewTableEntries != NULL) { + Process = HandleTable->QuotaProcess; + if (Process != NULL) { + try { + PsChargePoolQuota(Process, + PagedPool, + NewCountBytes - OldCountBytes); + + } except (EXCEPTION_EXECUTE_HANDLER) { + ExFreePool(NewTableEntries); + return NULL; + } + } + + // + // Initialize the free listhead. + // + + FreeHead = &NewTableEntries->ListEntry; + InitializeListHead(FreeHead); + + // + // If a new handle table is being created or an existing handle + // table is being extended, then initialize the free entry list + // allocated in the extension. + // + + NewTableBound = &NewTableEntries[NewCountEntries]; + if ((OldTableEntries == NULL) || (OldCountEntries != 0)) { + if (OldCountEntries == 0) { + FreeEntry = &NewTableEntries[1]; + + } else { + FreeEntry = &NewTableEntries[OldCountEntries]; + } + + do { + InsertTailList(FreeHead, &FreeEntry->ListEntry); + FreeEntry += 1; + } while (FreeEntry < NewTableBound); + + // + // If there is an old handle entry table, then move the old + // handle entires to the new handle entry table and free the + // old handle entry table. + // + + if (OldTableEntries != NULL) { + RtlMoveMemory(&NewTableEntries[1], + &OldTableEntries[1], + OldCountBytes - sizeof(HANDLE_ENTRY)); + + ExFreePool(OldTableEntries); + } + } + + // + // Set the new count of table entries, the new table bound, and + // the address of the new table entries. + // + + HandleTable->TableBound = NewTableBound; + HandleTable->TableEntries = NewTableEntries; + } + + return NewTableEntries; +} |