Files
mirror-processhacker/1.x/branches/ph-plugins/KProcessHacker/handle.c
T
wj32 7bd28e4357 moved branches, tags, trunk to 1.x branch
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2304 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2009-10-25 02:22:46 +00:00

356 lines
9.6 KiB
C

/*
* Process Hacker Driver -
* handle table
*
* Copyright (C) 2009 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include "include/handle.h"
#include "include/handlep.h"
NTSTATUS KphpAllocateHandleEntry(
__in PKPH_HANDLE_TABLE HandleTable,
__out PKPH_HANDLE_TABLE_ENTRY *Entry
);
NTSTATUS KphpFreeHandleEntry(
__in PKPH_HANDLE_TABLE HandleTable,
__in PKPH_HANDLE_TABLE_ENTRY Entry
);
/* KphCreateHandleTable
*
* Creates a handle table.
*
* HandleTable: A variable which receives a pointer to the handle table.
* MaximumHandles: The maximum number of handles that can be created.
* SizeOfEntry: The size of each handle table entry. This value must be
* divisible by 4.
* Tag: The tag to use when allocating handle table resources.
*/
NTSTATUS KphCreateHandleTable(
__out PKPH_HANDLE_TABLE *HandleTable,
__in ULONG MaximumHandles,
__in ULONG SizeOfEntry,
__in ULONG Tag
)
{
PKPH_HANDLE_TABLE handleTable;
/* Each handle entry must be at least the size of our
* handle table entry definition.
*/
if (SizeOfEntry < sizeof(KPH_HANDLE_TABLE_ENTRY))
return STATUS_INVALID_PARAMETER_3;
/* Handle entries must be 4-byte aligned. */
if (SizeOfEntry % 4 != 0)
return STATUS_INVALID_PARAMETER_3;
/* Allocate storage for the handle table structure. */
handleTable = ExAllocatePoolWithTag(
PagedPool,
sizeof(KPH_HANDLE_TABLE),
Tag
);
if (!handleTable)
return STATUS_INSUFFICIENT_RESOURCES;
/* Allocate storage for the handle table itself. */
handleTable->Table = ExAllocatePoolWithTag(
PagedPool,
MaximumHandles * SizeOfEntry,
Tag
);
if (!handleTable->Table)
{
ExFreePoolWithTag(handleTable, Tag);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize the rest of the table descriptor. */
handleTable->Tag = Tag;
handleTable->SizeOfEntry = SizeOfEntry;
handleTable->NextHandle = (HANDLE)0;
handleTable->FreeHandle = NULL;
ExInitializeFastMutex(&handleTable->Mutex);
handleTable->TableSize = MaximumHandles * SizeOfEntry;
/* Zero the handle table. */
memset(handleTable->Table, 0, handleTable->TableSize);
/* Pass the pointer to the handle table back. */
*HandleTable = handleTable;
return STATUS_SUCCESS;
}
/* KphFreeHandleTable
*
* Frees all handle table resources.
*/
VOID KphFreeHandleTable(
__in PKPH_HANDLE_TABLE HandleTable
)
{
ULONG i;
ULONG tag;
/* Free all handle values. */
for (i = 0; i < HandleTable->TableSize / HandleTable->SizeOfEntry; i++)
{
KphCloseHandle(HandleTable, KphHandleFromIndex(i));
}
/* Save the handle table tag first. */
tag = HandleTable->Tag;
/* Free the table. */
ExFreePoolWithTag(HandleTable->Table, tag);
/* Free the descriptor. */
ExFreePoolWithTag(HandleTable, tag);
}
/* KphCloseHandle
*
* Closes a handle, dereferencing the referenced object.
*/
NTSTATUS KphCloseHandle(
__in PKPH_HANDLE_TABLE HandleTable,
__in HANDLE Handle
)
{
NTSTATUS status = STATUS_SUCCESS;
PKPH_HANDLE_TABLE_ENTRY entry;
PVOID object;
if (!KphValidHandle(HandleTable, Handle, &entry))
return STATUS_INVALID_HANDLE;
/* Save a pointer to the object referenced by the handle. */
object = entry->Object;
/* Free the handle. */
status = KphpFreeHandleEntry(HandleTable, entry);
if (!NT_SUCCESS(status))
return status;
/* Dereference the object. */
KphDereferenceObject(object);
return status;
}
/* KphCreateHandle
*
* Creates a handle and references an object.
*/
NTSTATUS KphCreateHandle(
__in PKPH_HANDLE_TABLE HandleTable,
__in PVOID Object,
__out PHANDLE Handle
)
{
NTSTATUS status = STATUS_SUCCESS;
PKPH_HANDLE_TABLE_ENTRY entry;
/* Allocate a handle. */
status = KphpAllocateHandleEntry(HandleTable, &entry);
if (!NT_SUCCESS(status))
return status;
/* Reference and set the object in the entry. */
KphReferenceObject(Object);
entry->Object = Object;
/* Pass the handle back. */
*Handle = KphGetHandleEntry(entry);
return status;
}
/* KphReferenceObjectByHandle
*
* References an object from a handle.
*/
NTSTATUS KphReferenceObjectByHandle(
__in PKPH_HANDLE_TABLE HandleTable,
__in HANDLE Handle,
__in_opt PKPH_OBJECT_TYPE ObjectType,
__out PVOID *Object
)
{
NTSTATUS status = STATUS_SUCCESS;
PKPH_HANDLE_TABLE_ENTRY entry;
if (!KphValidHandle(HandleTable, Handle, &entry))
return STATUS_INVALID_HANDLE;
/* Lock the entry. */
if (!KphLockAllocatedHandleEntry(entry))
return STATUS_INVALID_HANDLE;
/* Check the type of object if the caller requested us
* to do that.
*/
if (ObjectType)
{
if (KphGetObjectType(entry->Object) != ObjectType)
{
/* Bad type. */
KphUnlockHandleEntry(entry);
return STATUS_OBJECT_TYPE_MISMATCH;
}
}
/* Reference and pass the object back. */
KphReferenceObject(entry->Object);
*Object = entry->Object;
KphUnlockHandleEntry(entry);
return status;
}
/* KphValidHandle
*
* Checks if a handle is valid.
*/
BOOLEAN KphValidHandle(
__in PKPH_HANDLE_TABLE HandleTable,
__in HANDLE Handle,
__out_opt PKPH_HANDLE_TABLE_ENTRY *Entry
)
{
PKPH_HANDLE_TABLE_ENTRY entry;
BOOLEAN valid;
entry = KphEntryFromHandle(HandleTable, Handle);
valid =
((ULONG_PTR)entry >= (ULONG_PTR)HandleTable->Table) &&
((ULONG_PTR)entry + HandleTable->SizeOfEntry <=
(ULONG_PTR)HandleTable->Table + HandleTable->TableSize);
if (valid)
*Entry = entry;
return valid;
}
/* KphpAllocateHandleEntry
*
* Allocates a handle table entry.
*/
NTSTATUS KphpAllocateHandleEntry(
__in PKPH_HANDLE_TABLE HandleTable,
__out PKPH_HANDLE_TABLE_ENTRY *Entry
)
{
NTSTATUS status = STATUS_SUCCESS;
PKPH_HANDLE_TABLE_ENTRY entry = NULL;
/* Prevent others from modifying the handle table. */
ExAcquireFastMutex(&HandleTable->Mutex);
/* Check the free list first. If we have a free entry,
* claim it and update the free list. Otherwise, create
* a new entry from the NextHandle value.
*/
if (HandleTable->FreeHandle)
{
/* We have a free entry. Update the free list. */
entry = HandleTable->FreeHandle;
/* The next free entry goes into FreeHandle. */
HandleTable->FreeHandle = KphGetNextFreeEntry(entry);
}
else
{
/* No free handles. We have to initialize a new one
* based on the NextHandle value.
*/
/* Make sure we don't go past the end of the table. */
if (
KphIndexFromHandle(HandleTable->NextHandle) *
HandleTable->SizeOfEntry <=
HandleTable->TableSize
)
{
/* Get a pointer to the entry from the handle. */
entry = KphEntryFromHandle(HandleTable, HandleTable->NextHandle);
/* Increment the next handle value. */
HandleTable->NextHandle = KphIncrementHandle(HandleTable->NextHandle);
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (NT_SUCCESS(status))
{
/* Set the entry's handle value. */
entry->Handle = KphHandleFromEntry(HandleTable, entry);
KphSetAllocatedEntry(entry);
*Entry = entry;
}
ExReleaseFastMutex(&HandleTable->Mutex);
return status;
}
/* KphpFreeHandleEntry
*
* Frees a handle table entry.
*/
NTSTATUS KphpFreeHandleEntry(
__in PKPH_HANDLE_TABLE HandleTable,
__in PKPH_HANDLE_TABLE_ENTRY Entry
)
{
ExAcquireFastMutex(&HandleTable->Mutex);
/* Lock the entry. */
if (!KphLockAllocatedHandleEntry(Entry))
{
/* Someone else has already freed the entry (or it was never allocated). */
ExReleaseFastMutex(&HandleTable->Mutex);
return STATUS_INVALID_HANDLE;
}
/* Mark the entry as unallocated. */
KphClearAllocatedEntry(Entry);
/* Add the entry to the free list. */
KphSetNextFreeEntry(Entry, HandleTable->FreeHandle);
HandleTable->FreeHandle = Entry;
/* Zero the entry (except for the Value). */
memset(&Entry->Object, 0, HandleTable->SizeOfEntry - sizeof(ULONG_PTR));
/* Unlock the entry. */
KphUnlockHandleEntry(Entry);
ExReleaseFastMutex(&HandleTable->Mutex);
return STATUS_SUCCESS;
}