NT4/private/ntos/ex/handle.c

1521 lines
40 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
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;
}