| /* |
| * Handle Tables |
| * |
| * Copyright (C) 2004 Robert Shearman |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #include "ntstatus.h" |
| #define WIN32_NO_STATUS |
| #include "windef.h" |
| #include "winternl.h" |
| #include "wine/debug.h" |
| #include "ntdll_misc.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(ntdll); |
| |
| /************************************************************************** |
| * RtlInitializeHandleTable (NTDLL.@) |
| * |
| * Initializes a handle table. |
| * |
| * PARAMS |
| * MaxHandleCount [I] The maximum number of handles the handle table will support. |
| * HandleSize [I] The size of each handle. |
| * HandleTable [I/O] The handle table. |
| * |
| * RETURNS |
| * Nothing. |
| * |
| * SEE |
| * RtlDestroyHandleTable(). |
| */ |
| void WINAPI RtlInitializeHandleTable(ULONG MaxHandleCount, ULONG HandleSize, RTL_HANDLE_TABLE * HandleTable) |
| { |
| TRACE("(%u, %u, %p)\n", MaxHandleCount, HandleSize, HandleTable); |
| |
| memset(HandleTable, 0, sizeof(*HandleTable)); |
| HandleTable->MaxHandleCount = MaxHandleCount; |
| HandleTable->HandleSize = HandleSize; |
| } |
| |
| /************************************************************************** |
| * RtlDestroyHandleTable (NTDLL.@) |
| * |
| * Destroys a handle table and frees associated resources. |
| * |
| * PARAMS |
| * HandleTable [I] The handle table. |
| * |
| * RETURNS |
| * Any status code returned by NtFreeVirtualMemory(). |
| * |
| * NOTES |
| * The native version of this API doesn't free the virtual memory that has |
| * been previously reserved, only the committed memory. There is no harm |
| * in also freeing the reserved memory because it won't have been handed out |
| * to any callers. I believe it is "more polite" to free everything. |
| * |
| * SEE |
| * RtlInitializeHandleTable(). |
| */ |
| NTSTATUS WINAPI RtlDestroyHandleTable(RTL_HANDLE_TABLE * HandleTable) |
| { |
| SIZE_T Size = 0; |
| |
| TRACE("(%p)\n", HandleTable); |
| |
| /* native version only releases committed memory, but we also release reserved */ |
| return NtFreeVirtualMemory( |
| NtCurrentProcess(), |
| &HandleTable->FirstHandle, |
| &Size, |
| MEM_RELEASE); |
| } |
| |
| /************************************************************************** |
| * RtlpAllocateSomeHandles (internal) |
| * |
| * Reserves memory for the handles if not previously done and commits memory |
| * for a batch of handles if none are free and adds them to the free list. |
| * |
| * PARAMS |
| * HandleTable [I/O] The handle table. |
| * |
| * RETURNS |
| * NTSTATUS code. |
| */ |
| static NTSTATUS RtlpAllocateSomeHandles(RTL_HANDLE_TABLE * HandleTable) |
| { |
| NTSTATUS status; |
| if (!HandleTable->FirstHandle) |
| { |
| PVOID FirstHandleAddr = NULL; |
| SIZE_T MaxSize = HandleTable->MaxHandleCount * HandleTable->HandleSize; |
| |
| /* reserve memory for the handles, but don't commit it yet because we |
| * probably won't use most of it and it will use up physical memory */ |
| status = NtAllocateVirtualMemory( |
| NtCurrentProcess(), |
| &FirstHandleAddr, |
| 0, |
| &MaxSize, |
| MEM_RESERVE, |
| PAGE_READWRITE); |
| if (status != STATUS_SUCCESS) |
| return status; |
| HandleTable->FirstHandle = FirstHandleAddr; |
| HandleTable->ReservedMemory = HandleTable->FirstHandle; |
| HandleTable->MaxHandle = (char *)HandleTable->FirstHandle + MaxSize; |
| } |
| if (!HandleTable->NextFree) |
| { |
| SIZE_T Offset, CommitSize = 4096; /* one page */ |
| RTL_HANDLE * FreeHandle = NULL; |
| PVOID NextAvailAddr = HandleTable->ReservedMemory; |
| |
| if (HandleTable->ReservedMemory >= HandleTable->MaxHandle) |
| return STATUS_NO_MEMORY; /* the handle table is completely full */ |
| |
| status = NtAllocateVirtualMemory( |
| NtCurrentProcess(), |
| &NextAvailAddr, |
| 0, |
| &CommitSize, |
| MEM_COMMIT, |
| PAGE_READWRITE); |
| if (status != STATUS_SUCCESS) |
| return status; |
| |
| for (Offset = 0; Offset < CommitSize; Offset += HandleTable->HandleSize) |
| { |
| /* make sure we don't go over handle limit, even if we can |
| * because of rounding of the table size up to the next page |
| * boundary */ |
| if ((char *)HandleTable->ReservedMemory + Offset >= (char *)HandleTable->MaxHandle) |
| break; |
| |
| FreeHandle = (RTL_HANDLE *)((char *)HandleTable->ReservedMemory + Offset); |
| |
| FreeHandle->Next = (RTL_HANDLE *)((char *)HandleTable->ReservedMemory + |
| Offset + HandleTable->HandleSize); |
| } |
| |
| /* shouldn't happen because we already test for this above, but |
| * handle it just in case */ |
| if (!FreeHandle) |
| return STATUS_NO_MEMORY; |
| |
| /* set the last handle's Next pointer to NULL so that when we run |
| * out of free handles we trigger another commit of memory and |
| * initialize the free pointers */ |
| FreeHandle->Next = NULL; |
| |
| HandleTable->NextFree = HandleTable->ReservedMemory; |
| |
| HandleTable->ReservedMemory = (char *)HandleTable->ReservedMemory + CommitSize; |
| } |
| return STATUS_SUCCESS; |
| } |
| |
| /************************************************************************** |
| * RtlAllocateHandle (NTDLL.@) |
| * |
| * Allocates a handle from the handle table. |
| * |
| * PARAMS |
| * HandleTable [I/O] The handle table. |
| * HandleIndex [O] Index of the handle returned. Optional. |
| * |
| * RETURNS |
| * Success: Pointer to allocated handle. |
| * Failure: NULL. |
| * |
| * NOTES |
| * A valid handle must have the bit set as indicated in the code below |
| * otherwise subsequent RtlIsValidHandle() calls will fail. |
| * |
| * static inline void RtlpMakeHandleAllocated(RTL_HANDLE * Handle) |
| * { |
| * ULONG_PTR *AllocatedBit = (ULONG_PTR *)(&Handle->Next); |
| * *AllocatedBit = *AllocatedBit | 1; |
| * } |
| * |
| * SEE |
| * RtlFreeHandle(). |
| */ |
| RTL_HANDLE * WINAPI RtlAllocateHandle(RTL_HANDLE_TABLE * HandleTable, ULONG * HandleIndex) |
| { |
| RTL_HANDLE * ret; |
| |
| TRACE("(%p, %p)\n", HandleTable, HandleIndex); |
| |
| if (!HandleTable->NextFree && RtlpAllocateSomeHandles(HandleTable) != STATUS_SUCCESS) |
| return NULL; |
| |
| ret = HandleTable->NextFree; |
| HandleTable->NextFree = ret->Next; |
| |
| if (HandleIndex) |
| *HandleIndex = (ULONG)(((PCHAR)ret - (PCHAR)HandleTable->FirstHandle) / HandleTable->HandleSize); |
| |
| return ret; |
| } |
| |
| /************************************************************************** |
| * RtlFreeHandle (NTDLL.@) |
| * |
| * Frees an allocated handle. |
| * |
| * PARAMS |
| * HandleTable [I/O] The handle table. |
| * Handle [I] The handle to be freed. |
| * |
| * RETURNS |
| * Success: TRUE. |
| * Failure: FALSE. |
| * |
| * SEE |
| * RtlAllocateHandle(). |
| */ |
| BOOLEAN WINAPI RtlFreeHandle(RTL_HANDLE_TABLE * HandleTable, RTL_HANDLE * Handle) |
| { |
| TRACE("(%p, %p)\n", HandleTable, Handle); |
| /* NOTE: we don't validate the handle and we don't make Handle->Next even |
| * again to signal that it is no longer in user - that is done as a side |
| * effect of setting Handle->Next to the previously next free handle in |
| * the handle table */ |
| memset(Handle, 0, HandleTable->HandleSize); |
| Handle->Next = HandleTable->NextFree; |
| HandleTable->NextFree = Handle; |
| return TRUE; |
| } |
| |
| /************************************************************************** |
| * RtlIsValidHandle (NTDLL.@) |
| * |
| * Determines whether a handle is valid or not. |
| * |
| * PARAMS |
| * HandleTable [I] The handle table. |
| * Handle [I] The handle to be tested. |
| * |
| * RETURNS |
| * Valid: TRUE. |
| * Invalid: FALSE. |
| */ |
| BOOLEAN WINAPI RtlIsValidHandle(const RTL_HANDLE_TABLE * HandleTable, const RTL_HANDLE * Handle) |
| { |
| TRACE("(%p, %p)\n", HandleTable, Handle); |
| /* make sure handle is within used region and that it is aligned on |
| * a HandleTable->HandleSize boundary and that Handle->Next is odd, |
| * indicating that the handle is active */ |
| if ((Handle >= (RTL_HANDLE *)HandleTable->FirstHandle) && |
| (Handle < (RTL_HANDLE *)HandleTable->ReservedMemory) && |
| !((ULONG_PTR)Handle & (HandleTable->HandleSize - 1)) && |
| ((ULONG_PTR)Handle->Next & 1)) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| /************************************************************************** |
| * RtlIsValidIndexHandle (NTDLL.@) |
| * |
| * Determines whether a handle index is valid or not. |
| * |
| * PARAMS |
| * HandleTable [I] The handle table. |
| * Index [I] The index of the handle to be tested. |
| * ValidHandle [O] The handle Index refers to. |
| * |
| * RETURNS |
| * Valid: TRUE. |
| * Invalid: FALSE. |
| */ |
| BOOLEAN WINAPI RtlIsValidIndexHandle(const RTL_HANDLE_TABLE * HandleTable, ULONG Index, RTL_HANDLE ** ValidHandle) |
| { |
| RTL_HANDLE * Handle; |
| |
| TRACE("(%p, %u, %p)\n", HandleTable, Index, ValidHandle); |
| Handle = (RTL_HANDLE *) |
| ((char *)HandleTable->FirstHandle + Index * HandleTable->HandleSize); |
| |
| if (RtlIsValidHandle(HandleTable, Handle)) |
| { |
| *ValidHandle = Handle; |
| return TRUE; |
| } |
| return FALSE; |
| } |