#include "nt.h" #include "ntdef.h" #include "ntrtl.h" #include "nturtl.h" #include "generichandles.h" NTSTATUS RtlpGenericTableAddSlots( PGENERIC_HANDLE_TABLE pCreatedTable, PGENERIC_HANDLE_SLOT pSlots, USHORT usSlots ) { NTSTATUS status = STATUS_SUCCESS; USHORT us; ASSERT(pCreatedTable != NULL); ASSERT(pSlots != NULL); ASSERT(usSlots > 0); RtlZeroMemory(pSlots, sizeof(GENERIC_HANDLE_SLOT) * usSlots); // // Next, next, next // for (us = 0; us < (usSlots - 1); us++) { pSlots[us].pNextFree = pSlots + (us + 1); } // // If there were no free slots, set this run as the new list of free // slots. Otherwise, set this as the "next available" free slot // if (pCreatedTable->pFirstFreeSlot != NULL) { pCreatedTable->pFirstFreeSlot->pNextFree = pSlots; } // // Add these to the list of slots // pCreatedTable->usSlotCount += usSlots; // // If there was no set of slots on the table already, then add these as the // current list of slots // if (pCreatedTable->pSlots == NULL) { pCreatedTable->pSlots = pSlots; } pCreatedTable->pFirstFreeSlot = pSlots; return status; } NTSTATUS RtlCreateGenericHandleTable( ULONG ulFlags, PGENERIC_HANDLE_TABLE pCreatedTable, PFNHANDLETABLEALLOC pfnAlloc, PFNHANDLETABLEFREE pfnFree, SIZE_T ulcbOriginalBlob, PVOID pvOriginalBlob ) { NTSTATUS status = STATUS_SUCCESS; if ((ulFlags != 0) || (pCreatedTable == NULL) || (ulcbOriginalBlob && !pvOriginalBlob)) { return STATUS_INVALID_PARAMETER; } RtlZeroMemory(pCreatedTable, sizeof(*pCreatedTable)); pCreatedTable->pfnAlloc = pfnAlloc; pCreatedTable->pfnFree = pfnFree; pCreatedTable->ulFlags = ulFlags; pCreatedTable->usInlineHandleSlots = (USHORT)(ulcbOriginalBlob / sizeof(GENERIC_HANDLE_SLOT)); // // If there were slots handed to us, then initialize the table to use those first // if (pCreatedTable->usInlineHandleSlots > 0) { pCreatedTable->pInlineHandleSlots = (PGENERIC_HANDLE_SLOT)pvOriginalBlob; // // Now, add the slots that we were handed into the free list // status = RtlpGenericTableAddSlots( pCreatedTable, pCreatedTable->pSlots, pCreatedTable->usInlineHandleSlots); } // // Otherwise, everything is zero-initted already, so just bop out // return status; } NTSTATUS RtlCreateGenericHandleTableInPlace( ULONG ulFlags, SIZE_T cbInPlace, PVOID pvPlace, PFNHANDLETABLEALLOC pfnAlloc, PFNHANDLETABLEFREE pfnFree, PGENERIC_HANDLE_TABLE *ppCreatedTable ) { NTSTATUS status; if ((pvPlace == NULL) || (cbInPlace && !pvPlace) || !ppCreatedTable) { return STATUS_INVALID_PARAMETER; } else if (cbInPlace < sizeof(GENERIC_HANDLE_TABLE)) { return STATUS_BUFFER_TOO_SMALL; } *ppCreatedTable = (PGENERIC_HANDLE_TABLE)pvPlace; status = RtlCreateGenericHandleTable( ulFlags, *ppCreatedTable, pfnAlloc, pfnFree, cbInPlace - sizeof(GENERIC_HANDLE_TABLE), *ppCreatedTable + 1); return status; } #define HANDLE_TABLE_SLOT_MASK (0x0000FFFF) #define HANDLE_TABLE_GEN_FLAG_SHIFT (16) #define HANDLE_TABLE_IN_USE_FLAG (0x8000) #define HANDLE_TABLE_GENERATION_MASK (~HANDLE_TABLE_IN_USE_FLAG) NTSTATUS RtlpFindSlotForHandle( PGENERIC_HANDLE_TABLE pHandleTable, PVOID pvHandle, PGENERIC_HANDLE_SLOT *ppSlot ) { PGENERIC_HANDLE_SLOT pSlot; USHORT usSlotEntry = (USHORT)((ULONG_PTR)pvHandle & HANDLE_TABLE_SLOT_MASK); USHORT usGeneration = (USHORT)((ULONG_PTR)pvHandle >> HANDLE_TABLE_GEN_FLAG_SHIFT); pSlot = pHandleTable->pSlots + usSlotEntry; // // Generation flag not in use, gen mismatch, or not in the table? Oops. // if (((usGeneration & HANDLE_TABLE_IN_USE_FLAG) == 0) || (usSlotEntry >= pHandleTable->usSlotCount) || (pSlot->usGenerationFlag != usGeneration)) { return STATUS_NOT_FOUND; } // // Return that the slot was found // else { *ppSlot = pSlot; return STATUS_SUCCESS; } } NTSTATUS RtlAddRefGenericHandle( PGENERIC_HANDLE_TABLE pHandleTable, PVOID pvGenericHandle ) { PGENERIC_HANDLE_SLOT pSlot; NTSTATUS status; if (pHandleTable == NULL) { return STATUS_INVALID_PARAMETER; } status = RtlpFindSlotForHandle(pHandleTable, pvGenericHandle, &pSlot); if (!NT_SUCCESS(status)) { return status; } pSlot->ulRefCount++; return STATUS_SUCCESS; } NTSTATUS RtlReleaseGenericHandle( PGENERIC_HANDLE_TABLE pHandleTable, PVOID pvGenericHandle ) { PGENERIC_HANDLE_SLOT pSlot; NTSTATUS status; if (pHandleTable == NULL) { return STATUS_INVALID_PARAMETER; } status = RtlpFindSlotForHandle(pHandleTable, pvGenericHandle, &pSlot); if (!NT_SUCCESS(status)) { return status; } pSlot->ulRefCount--; return STATUS_SUCCESS; } NTSTATUS RtlRemoveGenericHandle( PGENERIC_HANDLE_TABLE pHandleTable, ULONG ulFlags, PVOID pvObjectHandle ) { PGENERIC_HANDLE_SLOT pSlot = NULL; NTSTATUS status; if ((pHandleTable == NULL) || (ulFlags != 0)) { return STATUS_INVALID_PARAMETER; } status = RtlpFindSlotForHandle(pHandleTable, pvObjectHandle, &pSlot); if (!NT_SUCCESS(status)) { return status; } // // Flip the in-use flag // pSlot->usGenerationFlag &= ~HANDLE_TABLE_IN_USE_FLAG; pSlot->pNextFree = pHandleTable->pFirstFreeSlot; pHandleTable->pFirstFreeSlot = pSlot; return STATUS_SUCCESS; } NTSTATUS RtlDereferenceHandle( PGENERIC_HANDLE_TABLE pHandleTable, PVOID pvGenericHandle, PVOID *ppvObjectPointer ) { USHORT usSlotEntry; NTSTATUS status; PGENERIC_HANDLE_SLOT pSlot = NULL; if ((pHandleTable == NULL) || (pvGenericHandle == NULL) || (ppvObjectPointer == NULL)) { return STATUS_INVALID_PARAMETER; } *ppvObjectPointer = NULL; status = RtlpFindSlotForHandle(pHandleTable, pvGenericHandle, &pSlot); if (!NT_SUCCESS(status)) { return status; } *ppvObjectPointer = pSlot->pvThisHandle; return STATUS_SUCCESS; } NTSTATUS RtlpExpandGenericHandleTable( PGENERIC_HANDLE_TABLE pHandleTable, ULONG ulNewSlotCount ) { PGENERIC_HANDLE_SLOT pNewSlots = NULL; NTSTATUS status; // // New slot count is 0? Make it 20 instead. // if (ulNewSlotCount == 0) { ulNewSlotCount = pHandleTable->usSlotCount + 20; } // // Did we fly out of range? // if (ulNewSlotCount > 0xFFFF) { ulNewSlotCount = 0xFFFF; // // Can't allocate more, the table is full // if (ulNewSlotCount == pHandleTable->usSlotCount) { return STATUS_NO_MEMORY; } } // // Don't ever do this if there are free slots left in the table // ASSERT(pHandleTable->pFirstFreeSlot == NULL); status = pHandleTable->pfnAlloc(sizeof(GENERIC_HANDLE_SLOT) * ulNewSlotCount, (PVOID*)&pNewSlots); if (!NT_SUCCESS(status)) { return status; } return status; } NTSTATUS RtlAddGenericHandle( PGENERIC_HANDLE_TABLE pHandleTable, ULONG ulFlags, PVOID pvObject, PVOID *ppvObjectHandle ) { PGENERIC_HANDLE_SLOT pSlot = NULL; NTSTATUS status; if (ppvObjectHandle) *ppvObjectHandle = NULL; if ((pHandleTable == NULL) || (ulFlags != 0) || (pvObject != NULL) || (ppvObjectHandle == NULL)) { return STATUS_INVALID_PARAMETER; } if (pHandleTable->pFirstFreeSlot == NULL) { status = RtlpExpandGenericHandleTable(pHandleTable, (pHandleTable->usSlotCount * 3) / 2); if (!NT_SUCCESS(status)) { return status; } } ASSERT(pHandleTable->pFirstFreeSlot != NULL); // // Adjust free list // pSlot = pHandleTable->pFirstFreeSlot; pHandleTable->pFirstFreeSlot = pSlot->pNextFree; // // Set up the various flags. // ASSERT((pSlot->usGenerationFlag & HANDLE_TABLE_IN_USE_FLAG) == 0); // // Increment the generation flag, set the in-use flag // pSlot->usGenerationFlag = (pSlot->usGenerationFlag & HANDLE_TABLE_GENERATION_MASK) + 1; pSlot->usGenerationFlag |= HANDLE_TABLE_IN_USE_FLAG; pSlot->ulRefCount = 0; // // Record the object pointer // pSlot->pvThisHandle = pvObject; // // The object handle is composed of 16 bits of generation mask plus the top-bit set // (which nicely avoids people casting it to a pointer that they can use), and // the lower 16 bits of "slot number", or an index into the handle table. // *ppvObjectHandle = (PVOID)((ULONG_PTR)( (pSlot->usGenerationFlag << HANDLE_TABLE_GEN_FLAG_SHIFT) | ((pSlot - pHandleTable->pInlineHandleSlots) & HANDLE_TABLE_SLOT_MASK))); return STATUS_SUCCESS; }