| /* | |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.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("(%lu, %lu, %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) | |
| { | |
| ULONG 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; | |
| ULONG 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) | |
| { | |
| ULONG CommitSize = 4096; /* one page */ | |
| ULONG Offset; | |
| 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. | |
| * | |
| * 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 = (RTL_HANDLE *)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 = (RTL_HANDLE *)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, %lu, %p)\n", HandleTable, Index, ValidHandle); | |
| Handle = (RTL_HANDLE *) | |
| ((char *)HandleTable->FirstHandle + Index * HandleTable->HandleSize); | |
| if (RtlIsValidHandle(HandleTable, Handle)) | |
| { | |
| *ValidHandle = Handle; | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } |