- fixed handle table structures - implement generic executive handle tables (since there don't exist documents that describe the parameters of most of these functions (which are kernel internal only), i made them up as required) - adjusted OB's handle manager to use ex handle tables - adjusted the client id manager to use ex handle tables Modified: trunk/reactos/config Modified: trunk/reactos/include/ddk/kefuncs.h Modified: trunk/reactos/include/ntos/obtypes.h Modified: trunk/reactos/ntoskrnl/Makefile Added: trunk/reactos/ntoskrnl/ex/handle.c Modified: trunk/reactos/ntoskrnl/ex/init.c Modified: trunk/reactos/ntoskrnl/ex/sysinfo.c Modified: trunk/reactos/ntoskrnl/ex/work.c Modified: trunk/reactos/ntoskrnl/include/internal/ex.h Modified: trunk/reactos/ntoskrnl/include/internal/ob.h Modified: trunk/reactos/ntoskrnl/include/internal/ps.h Modified: trunk/reactos/ntoskrnl/io/iomgr.c Modified: trunk/reactos/ntoskrnl/io/pnpmgr.c Modified: trunk/reactos/ntoskrnl/lpc/close.c Modified: trunk/reactos/ntoskrnl/ob/handle.c Modified: trunk/reactos/ntoskrnl/ob/object.c Modified: trunk/reactos/ntoskrnl/ps/cid.c Modified: trunk/reactos/ntoskrnl/ps/create.c Modified: trunk/reactos/ntoskrnl/ps/idle.c Modified: trunk/reactos/ntoskrnl/ps/kill.c Modified: trunk/reactos/ntoskrnl/ps/process.c Modified: trunk/reactos/ntoskrnl/ps/psmgr.c Modified: trunk/reactos/ntoskrnl/ps/thread.c Modified: trunk/reactos/subsys/win32k/misc/object.c _____
Modified: trunk/reactos/config --- trunk/reactos/config 2005-03-13 13:59:06 UTC (rev 14006) +++ trunk/reactos/config 2005-03-13 14:21:47 UTC (rev 14007) @@ -15,22 +15,22 @@
# be optimized for. #
-OARCH := i486 +OARCH := i586
# # Whether to compile in the kernel debugger # -KDBG := 0 +KDBG := 1
# # Whether to compile for debugging # -DBG := 0 +DBG := 1
# # Whether to compile with optimizations # -OPTIMIZED := 0 +OPTIMIZED := 1
# # Whether to compile a multiprocessor or single processor version _____
Modified: trunk/reactos/include/ddk/kefuncs.h --- trunk/reactos/include/ddk/kefuncs.h 2005-03-13 13:59:06 UTC (rev 14006) +++ trunk/reactos/include/ddk/kefuncs.h 2005-03-13 14:21:47 UTC (rev 14007) @@ -42,6 +42,15 @@
#ifndef __USE_W32API
+static __inline +VOID +KeMemoryBarrier( + VOID) +{ + volatile LONG Barrier; + __asm__ __volatile__ ("xchg %%eax, %0" : : "m" (Barrier) : "%eax"); +} + VOID STDCALL KeAcquireSpinLockAtDpcLevel (IN PKSPIN_LOCK SpinLock);
#define KefAcquireSpinLockAtDpcLevel KeAcquireSpinLockAtDpcLevel _____
Modified: trunk/reactos/include/ntos/obtypes.h --- trunk/reactos/include/ntos/obtypes.h 2005-03-13 13:59:06 UTC (rev 14006) +++ trunk/reactos/include/ntos/obtypes.h 2005-03-13 14:21:47 UTC (rev 14007) @@ -87,18 +87,42 @@
PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
+typedef struct _HANDLE_TABLE_ENTRY_INFO { + ULONG AuditMask; +} HANDLE_TABLE_ENTRY_INFO, *PHANDLE_TABLE_ENTRY_INFO; + +typedef struct _HANDLE_TABLE_ENTRY { + union { + PVOID Object; + ULONG_PTR ObAttributes; + PHANDLE_TABLE_ENTRY_INFO InfoTable; + ULONG_PTR Value; + } u1; + union { + ULONG GrantedAccess; + USHORT GrantedAccessIndex; + LONG NextFreeTableEntry; + } u2; +} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY; + #endif /* __USE_W32API */
typedef struct _HANDLE_TABLE { - LIST_ENTRY ListHead; - KSPIN_LOCK ListLock; -} HANDLE_TABLE; + ULONG Flags; + LONG HandleCount; + PHANDLE_TABLE_ENTRY **Table; + PEPROCESS QuotaProcess; + HANDLE UniqueProcessId; + LONG FirstFreeTableEntry; + LONG NextIndexNeedingPool; + ERESOURCE HandleTableLock; + LIST_ENTRY HandleTableList; + KEVENT HandleContentionEvent; +} HANDLE_TABLE, *PHANDLE_TABLE;
#ifndef __USE_W32API
-typedef struct _HANDLE_TABLE *PHANDLE_TABLE; - /* * FIXME: These will eventually become centerfold in the compliant Ob Manager * For now, they are only here so Device Map is properly defined before the header _____
Modified: trunk/reactos/ntoskrnl/Makefile --- trunk/reactos/ntoskrnl/Makefile 2005-03-13 13:59:06 UTC (rev 14006) +++ trunk/reactos/ntoskrnl/Makefile 2005-03-13 14:21:47 UTC (rev 14007) @@ -239,6 +239,7 @@
ex/event.o \ ex/evtpair.o \ ex/fmutex.o \ + ex/handle.o \ ex/hashtab.o \ ex/init.o \ ex/interlck.o \ _____
Added: trunk/reactos/ntoskrnl/ex/handle.c --- trunk/reactos/ntoskrnl/ex/handle.c 2005-03-13 13:59:06 UTC (rev 14006) +++ trunk/reactos/ntoskrnl/ex/handle.c 2005-03-13 14:21:47 UTC (rev 14007) @@ -0,0 +1,949 @@
+/* $Id:$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/ex/handle.c + * PURPOSE: Generic Executive Handle Tables + * + * PROGRAMMERS: Thomas Weidenmueller w3seek@reactos.com + * + * TODO: + * + * - the last entry of a subhandle list should be reserved for auditing + * + * ExSweepHandleTable (???) + * ExReferenceHandleDebugInfo + * ExSnapShotHandleTables + * ExpMoveFreeHandles (???) + */ + +/* INCLUDES *****************************************************************/ + +#include <ntoskrnl.h> + +#define NDEBUG +#include <internal/debug.h> + +static LIST_ENTRY ExpHandleTableHead; +static FAST_MUTEX ExpHandleTableListLock; +static LARGE_INTEGER ExpHandleShortWait; + +#define ExAcquireHandleTableListLock() \ + ExAcquireFastMutexUnsafe(&ExpHandleTableListLock) + +#define ExReleaseHandleTableListLock() \ + ExReleaseFastMutexUnsafe(&ExpHandleTableListLock) + +#define ExAcquireHandleTableLockExclusive(HandleTable) \ + ExAcquireResourceExclusiveLite(&(HandleTable)->HandleTableLock, TRUE) + +#define ExAcquireHandleTableLockShared(HandleTable) \ + ExAcquireResourceSharedLite(&(HandleTable)->HandleTableLock, TRUE) + +#define ExReleaseHandleTableLock(HandleTable) \ + ExReleaseResourceLite(&(HandleTable)->HandleTableLock) + +/* + 5 bits: reserved + 8 bits: top level index + 10 bits: middle level index + 9 bits: sub handle index +*/ +#define N_TLI_BITS 8 /* top level index */ +#define N_MLI_BITS 10 /* middle level index */ +#define N_EI_BITS 9 /* sub handle index */ +#define TLI_OFFSET (N_MLI_BITS + N_EI_BITS) +#define MLI_OFFSET N_EI_BITS +#define EI_OFFSET 0 + +#define N_TOPLEVEL_POINTERS (1 << N_TLI_BITS) +#define N_MIDDLELEVEL_POINTERS (1 << N_MLI_BITS) +#define N_SUBHANDLE_ENTRIES (1 << N_EI_BITS) +#define EX_MAX_HANDLES (N_TOPLEVEL_POINTERS * N_MIDDLELEVEL_POINTERS * N_SUBHANDLE_ENTRIES) + +#define VALID_HANDLE_MASK (((N_TOPLEVEL_POINTERS - 1) << TLI_OFFSET) | \ + ((N_MIDDLELEVEL_POINTERS - 1) << MLI_OFFSET) | ((N_SUBHANDLE_ENTRIES - 1) << EI_OFFSET)) +#define TLI_FROM_HANDLE(index) (ULONG)(((index) >> TLI_OFFSET) & (N_TOPLEVEL_POINTERS - 1)) +#define MLI_FROM_HANDLE(index) (ULONG)(((index) >> MLI_OFFSET) & (N_MIDDLELEVEL_POINTERS - 1)) +#define ELI_FROM_HANDLE(index) (ULONG)(((index) >> EI_OFFSET) & (N_SUBHANDLE_ENTRIES - 1)) + +#define N_MAX_HANDLE (N_TOPLEVEL_POINTERS * N_MIDDLELEVEL_POINTERS * N_SUBHANDLE_ENTRIES) + +#define BUILD_HANDLE(tli, mli, eli) ((((tli) & (N_TOPLEVEL_POINTERS - 1)) << TLI_OFFSET) | \ + (((mli) & (N_MIDDLELEVEL_POINTERS - 1)) << MLI_OFFSET) | (((eli) & (N_SUBHANDLE_ENTRIES - 1)) << EI_OFFSET)) + +#define IS_INVALID_EX_HANDLE(index) \ + (((index) & ~VALID_HANDLE_MASK) != 0) +#define IS_VALID_EX_HANDLE(index) \ + (((index) & ~VALID_HANDLE_MASK) == 0) + +/********************************************************************** ********/ + +VOID +ExpInitializeHandleTables(VOID) +{ + ExpHandleShortWait.QuadPart = -50000; + InitializeListHead(&ExpHandleTableHead); + ExInitializeFastMutex(&ExpHandleTableListLock); +} + +PHANDLE_TABLE +ExCreateHandleTable(IN PEPROCESS QuotaProcess OPTIONAL) +{ + PHANDLE_TABLE HandleTable; + + PAGED_CODE(); + + if(QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + /* allocate enough memory for the handle table and the lowest level */ + HandleTable = ExAllocatePoolWithTag(NonPagedPool, + sizeof(HANDLE_TABLE) + (N_TOPLEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY*)), + TAG('E', 'x', 'H', 't')); + if(HandleTable != NULL) + { + /* initialize the handle table */ + HandleTable->Flags = 0; + HandleTable->HandleCount = 0; + HandleTable->Table = (PHANDLE_TABLE_ENTRY**)(HandleTable + 1); + HandleTable->QuotaProcess = QuotaProcess; + HandleTable->FirstFreeTableEntry = -1; /* no entries freed so far */ + HandleTable->NextIndexNeedingPool = 0; /* no entries freed so far, so we have to allocate already for the first handle */ + HandleTable->UniqueProcessId = (QuotaProcess ? QuotaProcess->UniqueProcessId : PsGetCurrentProcessId()); + + ExInitializeResource(&HandleTable->HandleTableLock); + + KeInitializeEvent(&HandleTable->HandleContentionEvent, + NotificationEvent, + FALSE); + + RtlZeroMemory(HandleTable->Table, N_TOPLEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY*)); + + /* during bootup KeGetCurrentThread() might be NULL, needs to be fixed... */ + if(KeGetCurrentThread() != NULL) + { + /* insert it into the global handle table list */ + KeEnterCriticalRegion(); + + ExAcquireHandleTableListLock(); + InsertTailList(&ExpHandleTableHead, + &HandleTable->HandleTableList); + ExReleaseHandleTableListLock(); + + KeLeaveCriticalRegion(); + } + else + { + InsertTailList(&ExpHandleTableHead, + &HandleTable->HandleTableList); + } + } + else + { + /* FIXME - return the quota to the process */ + } + + return HandleTable; +} + +static BOOLEAN +ExLockHandleTableEntryNoDestructionCheck(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + ULONG_PTR Current, New; + + PAGED_CODE(); + + DPRINT("Entering handle table entry 0x%x lock...\n", Entry); + + ASSERT(HandleTable); + ASSERT(Entry); + + for(;;) + { + Current = (volatile ULONG_PTR)Entry->u1.Object; + + if(!Current) + { + DPRINT("Attempted to lock empty handle table entry 0x%x or handle table shut down\n", Entry); + break; + } + + if(!(Current & EX_HANDLE_ENTRY_LOCKED)) + { + New = Current | EX_HANDLE_ENTRY_LOCKED; + if(InterlockedCompareExchangePointer(&Entry->u1.Object, + (PVOID)New, + (PVOID)Current) == (PVOID)Current) + { + DPRINT("SUCCESS handle table 0x%x entry 0x%x lock\n", HandleTable, Entry); + /* we acquired the lock */ + return TRUE; + } + } + + /* wait about 5ms at maximum so we don't wait forever in unfortunate + co-incidences where releasing the lock in another thread happens right + before we're waiting on the contention event to get pulsed, which might + never happen again... */ + KeWaitForSingleObject(&HandleTable->HandleContentionEvent, + Executive, + KernelMode, + FALSE, + &ExpHandleShortWait); + } + + return FALSE; +} + +VOID +ExDestroyHandleTable(IN PHANDLE_TABLE HandleTable, + IN PEX_DESTROY_HANDLE_CALLBACK DestroyHandleCallback OPTIONAL, + IN PVOID Context OPTIONAL) +{ + PHANDLE_TABLE_ENTRY **tlp, **lasttlp, *mlp, *lastmlp; + PEPROCESS QuotaProcess; + + PAGED_CODE(); + + ASSERT(HandleTable); + + KeEnterCriticalRegion(); + + /* ensure there's no other operations going by acquiring an exclusive lock */ + ExAcquireHandleTableLockExclusive(HandleTable); + + ASSERT(!(HandleTable->Flags & EX_HANDLE_TABLE_CLOSING)); + + HandleTable->Flags |= EX_HANDLE_TABLE_CLOSING; + + KePulseEvent(&HandleTable->HandleContentionEvent, + EVENT_INCREMENT, + FALSE); + + /* remove the handle table from the global handle table list */ + ExAcquireHandleTableListLock(); + RemoveEntryList(&HandleTable->HandleTableList); + ExReleaseHandleTableListLock(); + + /* call the callback function to cleanup the objects associated with the + handle table */ + if(DestroyHandleCallback != NULL) + { + for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS; + tlp != lasttlp; + tlp++) + { + if((*tlp) != NULL) + { + for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS; + mlp != lastmlp; + mlp++) + { + if((*mlp) != NULL) + { + PHANDLE_TABLE_ENTRY curee, laste; + + for(curee = *mlp, laste = *mlp + N_SUBHANDLE_ENTRIES; + curee != laste; + curee++) + { + if(curee->u1.Object != NULL && ExLockHandleTableEntryNoDestructionCheck(HandleTable, curee)) + { + DestroyHandleCallback(HandleTable, curee->u1.Object, curee->u2.GrantedAccess, Context); + ExUnlockHandleTableEntry(HandleTable, curee); + } + } + } + } + } + } + } + + QuotaProcess = HandleTable->QuotaProcess; + + /* free the tables */ + for(tlp = HandleTable->Table, lasttlp = HandleTable->Table + N_TOPLEVEL_POINTERS; + tlp != lasttlp; + tlp++) + { + if((*tlp) != NULL) + { + for(mlp = *tlp, lastmlp = (*tlp) + N_MIDDLELEVEL_POINTERS; + mlp != lastmlp; + mlp++) + { + if((*mlp) != NULL) + { + ExFreePool(*mlp); + + if(QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } + } + } + + ExFreePool(*tlp); + + if(QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } + } + } + + ExReleaseHandleTableLock(HandleTable); + + KeLeaveCriticalRegion(); + + /* free the handle table */ + ExDeleteResource(&HandleTable->HandleTableLock); + ExFreePool(HandleTable); + + if(QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } +} + +PHANDLE_TABLE +ExDupHandleTable(IN PEPROCESS QuotaProcess OPTIONAL, + IN PEX_DUPLICATE_HANDLE_CALLBACK DuplicateHandleCallback OPTIONAL, + IN PVOID Context OPTIONAL, + IN PHANDLE_TABLE SourceHandleTable) +{ + PHANDLE_TABLE HandleTable; + + PAGED_CODE(); + + ASSERT(SourceHandleTable); + + HandleTable = ExCreateHandleTable(QuotaProcess); + if(HandleTable != NULL) + { + PHANDLE_TABLE_ENTRY **tlp, **srctlp, **etlp, *mlp, *srcmlp, *emlp, stbl, srcstbl, estbl; + LONG tli, mli, eli; + + tli = mli = eli = 0; + + /* make sure the other handle table isn't being changed during the duplication */ + ExAcquireHandleTableLockShared(SourceHandleTable); + + /* allocate enough tables */ + etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS; + for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table; + srctlp != etlp; + srctlp++, tlp++) + { + if(*srctlp != NULL) + { + /* allocate middle level entry tables */ + if(QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + *tlp = ExAllocatePoolWithTag(PagedPool, + N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(*tlp != NULL) + { + RtlZeroMemory(*tlp, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY)); + + KeMemoryBarrier(); + + emlp = *srctlp + N_MIDDLELEVEL_POINTERS; + for(srcmlp = *srctlp, mlp = *tlp; + srcmlp != emlp; + srcmlp++, mlp++) + { + if(*srcmlp != NULL) + { + /* allocate subhandle tables */ + if(QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + *mlp = ExAllocatePoolWithTag(PagedPool, + N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(*mlp != NULL) + { + RtlZeroMemory(*mlp, N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY)); + } + else + { + goto freehandletable; + } + } + else + { + *mlp = NULL; + } + } + } + else + { +freehandletable: + DPRINT1("Failed to duplicate handle table 0x%x\n", SourceHandleTable); + + ExReleaseHandleTableLock(SourceHandleTable); + + ExDestroyHandleTable(HandleTable, + NULL, + NULL); + /* allocate an empty handle table */ + return ExCreateHandleTable(QuotaProcess); + } + } + } + + /* duplicate the handles */ + HandleTable->HandleCount = SourceHandleTable->HandleCount; + HandleTable->FirstFreeTableEntry = SourceHandleTable->FirstFreeTableEntry; + HandleTable->NextIndexNeedingPool = SourceHandleTable->NextIndexNeedingPool; + + /* make sure all tables are zeroed */ + KeMemoryBarrier(); + + etlp = SourceHandleTable->Table + N_TOPLEVEL_POINTERS; + for(srctlp = SourceHandleTable->Table, tlp = HandleTable->Table; + srctlp != etlp; + srctlp++, tlp++, tli++) + { + if(*srctlp != NULL) + { + ASSERT(*tlp != NULL); + + emlp = *srctlp + N_MIDDLELEVEL_POINTERS; + for(srcmlp = *srctlp, mlp = *tlp; + srcmlp != emlp; + srcmlp++, mlp++, mli++) + { + if(*srcmlp != NULL) + { + ASSERT(*mlp != NULL); + + /* walk all handle entries and duplicate them if wanted */ + estbl = *srcmlp + N_SUBHANDLE_ENTRIES; + for(srcstbl = *srcmlp, stbl = *mlp; + srcstbl != estbl; + srcstbl++, stbl++, eli++) + { + /* try to duplicate the source handle */ + if(srcstbl->u1.Object != NULL && + ExLockHandleTableEntry(SourceHandleTable, + srcstbl)) + { + /* ask the caller if this handle should be duplicated */ + if(DuplicateHandleCallback != NULL && + !DuplicateHandleCallback(HandleTable, + srcstbl, + Context)) + { + /* free the entry and chain it into the free list */ + HandleTable->HandleCount--; + stbl->u1.Object = NULL; + stbl->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry; + HandleTable->FirstFreeTableEntry = BUILD_HANDLE(tli, mli, eli); + } + else + { + /* duplicate the handle and unlock it */ + stbl->u2.GrantedAccess = srcstbl->u2.GrantedAccess; + stbl->u1.ObAttributes = srcstbl->u1.ObAttributes & ~EX_HANDLE_ENTRY_LOCKED; + } + ExUnlockHandleTableEntry(SourceHandleTable, + srcstbl); + } + else + { + /* this is a free handle table entry, copy over the entire + structure as-is */ + *stbl = *srcstbl; + } + } + } + } + } + } + + /* release the source handle table */ + ExReleaseHandleTableLock(SourceHandleTable); + } + + return HandleTable; +} + +static PHANDLE_TABLE_ENTRY +ExpAllocateHandleTableEntry(IN PHANDLE_TABLE HandleTable, + OUT PLONG Handle) +{ + PHANDLE_TABLE_ENTRY Entry = NULL; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Handle); + ASSERT(KeGetCurrentThread() != NULL); + + DPRINT("HT[0x%x]: HandleCount: %d\n", HandleTable, HandleTable->HandleCount); + + if(HandleTable->HandleCount < EX_MAX_HANDLES) + { + ULONG tli, mli, eli; + + if(HandleTable->FirstFreeTableEntry != -1) + { + /* there's a free handle entry we can just grab and use */ + tli = TLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry); + mli = MLI_FROM_HANDLE(HandleTable->FirstFreeTableEntry); + eli = ELI_FROM_HANDLE(HandleTable->FirstFreeTableEntry); + + /* the pointer should be valid in any way!!! */ + ASSERT(HandleTable->Table[tli]); + ASSERT(HandleTable->Table[tli][mli]); + + Entry = &HandleTable->Table[tli][mli][eli]; + + *Handle = HandleTable->FirstFreeTableEntry; + + /* save the index to the next free handle (if available) */ + HandleTable->FirstFreeTableEntry = Entry->u2.NextFreeTableEntry; + Entry->u2.NextFreeTableEntry = 0; + Entry->u1.Object = NULL; + + HandleTable->HandleCount++; + } + else + { + /* we need to allocate a new subhandle table first */ + PHANDLE_TABLE_ENTRY cure, laste, ntbl, *nmtbl; + ULONG i; + BOOLEAN AllocatedMtbl; + + ASSERT(HandleTable->NextIndexNeedingPool <= N_MAX_HANDLE); + + /* the index of the next table to be allocated was saved in + NextIndexNeedingPool the last time a handle entry was allocated and + the subhandle entry list was full. the subhandle entry index of + NextIndexNeedingPool should be 0 here! */ + tli = TLI_FROM_HANDLE(HandleTable->NextIndexNeedingPool); + mli = MLI_FROM_HANDLE(HandleTable->NextIndexNeedingPool); + DPRINT("HandleTable->NextIndexNeedingPool: 0x%x\n", HandleTable->NextIndexNeedingPool); + DPRINT("tli: 0x%x mli: 0x%x eli: 0x%x\n", tli, mli, ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool)); + + ASSERT(ELI_FROM_HANDLE(HandleTable->NextIndexNeedingPool) == 0); + + DPRINT("HandleTable->Table[%d] == 0x%x\n", tli, HandleTable->Table[tli]); + + /* allocate a middle level entry list if required */ + nmtbl = HandleTable->Table[tli]; + if(nmtbl == NULL) + { + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + nmtbl = ExAllocatePoolWithTag(PagedPool, + N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(nmtbl == NULL) + { + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - return the quota to the process */ + } + + return NULL; + } + + /* clear the middle level entry list */ + RtlZeroMemory(nmtbl, N_MIDDLELEVEL_POINTERS * sizeof(PHANDLE_TABLE_ENTRY)); + + /* make sure the table was zeroed before we set one item */ + KeMemoryBarrier(); + + /* note, don't set the the pointer in the top level list yet because we + might screw up lookups if allocating a subhandle entry table failed + and this newly allocated table might get freed again */ + AllocatedMtbl = TRUE; + } + else + { + AllocatedMtbl = FALSE; + + /* allocate a subhandle entry table in any case! */ + ASSERT(nmtbl[mli] == NULL); + } + + DPRINT("HandleTable->Table[%d][%d] == 0x%x\n", tli, mli, nmtbl[mli]); + + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Charge process quota before allocating the handle table! */ + } + + ntbl = ExAllocatePoolWithTag(PagedPool, + N_SUBHANDLE_ENTRIES * sizeof(HANDLE_TABLE_ENTRY), + TAG('E', 'x', 'H', 't')); + if(ntbl == NULL) + { + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Return process quota charged */ + } + + /* free the middle level entry list, if allocated, because it's empty and + unused */ + if(AllocatedMtbl) + { + ExFreePool(nmtbl); + + if(HandleTable->QuotaProcess != NULL) + { + /* FIXME - Return process quota charged */ + } + } + + return NULL; + } + + /* let's just use the very first entry */ + Entry = ntbl; + Entry->u1.ObAttributes = EX_HANDLE_ENTRY_LOCKED; + Entry->u2.NextFreeTableEntry = 0; + + *Handle = HandleTable->NextIndexNeedingPool; + + HandleTable->HandleCount++; + + /* set the FirstFreeTableEntry member to the second entry and chain the + free entries */ + HandleTable->FirstFreeTableEntry = HandleTable->NextIndexNeedingPool + 1; + for(cure = Entry + 1, laste = Entry + N_SUBHANDLE_ENTRIES, i = HandleTable->FirstFreeTableEntry + 1; + cure != laste; + cure++, i++) + { + cure->u1.Object = NULL; + cure->u2.NextFreeTableEntry = i; + } + /* truncate the free entry list */ + (cure - 1)->u2.NextFreeTableEntry = -1; + + /* save the pointers to the allocated list(s) */ + InterlockedExchangePointer(&nmtbl[mli], ntbl); + if(AllocatedMtbl) + { + InterlockedExchangePointer(&HandleTable->Table[tli], nmtbl); + } + + /* increment the NextIndexNeedingPool to the next index where we need to + allocate new memory */ + HandleTable->NextIndexNeedingPool += N_SUBHANDLE_ENTRIES; + } + } + else + { + DPRINT1("Can't allocate any more handles in handle table 0x%x!\n", HandleTable); + } + + return Entry; +} + +static VOID +ExpFreeHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry, + IN LONG Handle) +{ + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + ASSERT(IS_VALID_EX_HANDLE(Handle)); + + DPRINT("ExpFreeHandleTableEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry); + + /* automatically unlock the entry if currently locked. We however don't notify + anyone who waited on the handle because we're holding an exclusive lock after + all and these locks will fail then */ + InterlockedExchangePointer(&Entry->u1.Object, NULL); + Entry->u2.NextFreeTableEntry = HandleTable->FirstFreeTableEntry; + HandleTable->FirstFreeTableEntry = Handle; + + HandleTable->HandleCount--; +} + +static PHANDLE_TABLE_ENTRY +ExpLookupHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN LONG Handle) +{ + PHANDLE_TABLE_ENTRY Entry = NULL; + + PAGED_CODE(); + + ASSERT(HandleTable); + + if(IS_VALID_EX_HANDLE(Handle)) + { + ULONG tli, mli, eli; + PHANDLE_TABLE_ENTRY *mlp; + + tli = TLI_FROM_HANDLE(Handle); + mli = MLI_FROM_HANDLE(Handle); + eli = ELI_FROM_HANDLE(Handle); + + mlp = HandleTable->Table[tli]; + if(Handle < HandleTable->NextIndexNeedingPool && + mlp != NULL && mlp[mli] != NULL && mlp[mli][eli].u1.Object != NULL) + { + Entry = &mlp[mli][eli]; + DPRINT("handle lookup 0x%x -> entry 0x%x [HT:0x%x] ptr: 0x%x\n", Handle, Entry, HandleTable, mlp[mli][eli].u1.Object); + } + } + else + { + DPRINT("Looking up invalid handle 0x%x\n", Handle); + } + + return Entry; +} + +BOOLEAN +ExLockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + ULONG_PTR Current, New; + + PAGED_CODE(); + + DPRINT("Entering handle table entry 0x%x lock...\n", Entry); + + ASSERT(HandleTable); + ASSERT(Entry); + + for(;;) + { + Current = (volatile ULONG_PTR)Entry->u1.Object; + + if(!Current || (HandleTable->Flags & EX_HANDLE_TABLE_CLOSING)) + { + DPRINT("Attempted to lock empty handle table entry 0x%x or handle table shut down\n", Entry); + break; + } + + if(!(Current & EX_HANDLE_ENTRY_LOCKED)) + { + New = Current | EX_HANDLE_ENTRY_LOCKED; + if(InterlockedCompareExchangePointer(&Entry->u1.Object, + (PVOID)New, + (PVOID)Current) == (PVOID)Current) + { + DPRINT("SUCCESS handle table 0x%x entry 0x%x lock\n", HandleTable, Entry); + /* we acquired the lock */ + return TRUE; + } + } + + /* wait about 5ms at maximum so we don't wait forever in unfortunate + co-incidences where releasing the lock in another thread happens right + before we're waiting on the contention event to get pulsed, which might + never happen again... */ + KeWaitForSingleObject(&HandleTable->HandleContentionEvent, + Executive, + KernelMode, + FALSE, + &ExpHandleShortWait); + } + + return FALSE; +} + +VOID +ExUnlockHandleTableEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + ULONG_PTR Current, New; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + + DPRINT("ExUnlockHandleTableEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry); + + Current = (volatile ULONG_PTR)Entry->u1.Object; + + ASSERT(Current & EX_HANDLE_ENTRY_LOCKED); + + New = Current & ~EX_HANDLE_ENTRY_LOCKED; + + InterlockedExchangePointer(&Entry->u1.Object, + (PVOID)New); + + /* we unlocked the entry, pulse the contention event so threads who're waiting + on the release can continue */ + KePulseEvent(&HandleTable->HandleContentionEvent, + EVENT_INCREMENT, + FALSE); +} + +LONG +ExCreateHandle(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry) +{ + PHANDLE_TABLE_ENTRY NewHandleTableEntry; + LONG Handle = EX_INVALID_HANDLE; + + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + + /* The highest bit in Entry->u1.Object has to be 1 so we make sure it's a + pointer to kmode memory. It will cleared though because it also indicates + the lock */ + ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED); + + KeEnterCriticalRegion(); + ExAcquireHandleTableLockExclusive(HandleTable); + + NewHandleTableEntry = ExpAllocateHandleTableEntry(HandleTable, + &Handle); + if(NewHandleTableEntry != NULL) + { + *NewHandleTableEntry = *Entry; + + ExUnlockHandleTableEntry(HandleTable, + NewHandleTableEntry); + } + + ExReleaseHandleTableLock(HandleTable); + KeLeaveCriticalRegion(); + + return Handle; +} + +BOOLEAN +ExDestroyHandle(IN PHANDLE_TABLE HandleTable, + IN LONG Handle) +{ + PHANDLE_TABLE_ENTRY HandleTableEntry; + BOOLEAN Ret = FALSE; + + PAGED_CODE(); + + ASSERT(HandleTable); + + KeEnterCriticalRegion(); + ExAcquireHandleTableLockExclusive(HandleTable); + + HandleTableEntry = ExpLookupHandleTableEntry(HandleTable, + Handle); + + if(HandleTableEntry != NULL && ExLockHandleTableEntry(HandleTable, HandleTableEntry)) + { + /* free and automatically unlock the handle. However we don't need to pulse + the contention event since other locks on this entry will fail */ + ExpFreeHandleTableEntry(HandleTable, + HandleTableEntry, + Handle); + Ret = TRUE; + } + + ExReleaseHandleTableLock(HandleTable); + KeLeaveCriticalRegion(); + + return Ret; +} + +VOID +ExDestroyHandleByEntry(IN PHANDLE_TABLE HandleTable, + IN PHANDLE_TABLE_ENTRY Entry, + IN LONG Handle) +{ + PAGED_CODE(); + + ASSERT(HandleTable); + ASSERT(Entry); + + /* This routine requires the entry to be locked */ + ASSERT((ULONG_PTR)Entry->u1.Object & EX_HANDLE_ENTRY_LOCKED); + + DPRINT("DestroyHandleByEntry HT:0x%x Entry:0x%x\n", HandleTable, Entry); + [truncated at 1000 lines; 2866 more skipped]