- 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]