From ee6982eeae1d42e183f1920b39e0929fb22cc957 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Tue, 7 Dec 2004 14:47:13 +0000 Subject: [PATCH] Implement handle tables and add tests for them. --- dlls/ntdll/Makefile.in | 1 + dlls/ntdll/handletable.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/ntdll.spec | 10 +- dlls/ntdll/tests/rtl.c | 47 +++++++- include/winternl.h | 24 ++++ 5 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 dlls/ntdll/handletable.c diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index cdd7a92b2aa..21ba9d433e7 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -16,6 +16,7 @@ C_SRCS = \ error.c \ exception.c \ file.c \ + handletable.c \ heap.c \ large_int.c \ loader.c \ diff --git a/dlls/ntdll/handletable.c b/dlls/ntdll/handletable.c new file mode 100644 index 00000000000..2baa749dd85 --- /dev/null +++ b/dlls/ntdll/handletable.c @@ -0,0 +1,296 @@ +/* + * 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 + +#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( + GetCurrentProcess(), + &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( + GetCurrentProcess(), + &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( + GetCurrentProcess(), + &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; +} diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index ca926a23d52..7d7d212e802 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -295,6 +295,7 @@ @ stdcall RtlAddVectoredExceptionHandler(long ptr) @ stdcall RtlAdjustPrivilege(long long long long) @ stdcall RtlAllocateAndInitializeSid (ptr long long long long long long long long long ptr) +@ stdcall RtlAllocateHandle(ptr ptr) @ stdcall RtlAllocateHeap(long long long) @ stub RtlAnsiCharToUnicodeChar @ stdcall RtlAnsiStringToUnicodeSize(ptr) @@ -372,6 +373,7 @@ @ stdcall RtlDeleteResource(ptr) @ stdcall RtlDeleteSecurityObject(long) @ stdcall RtlDestroyEnvironment(ptr) +@ stdcall RtlDestroyHandleTable(ptr) @ stdcall RtlDestroyHeap(long) @ stdcall RtlDestroyProcessParameters(ptr) @ stdcall RtlDestroyQueryDebugBuffer(ptr) @@ -430,7 +432,7 @@ @ stdcall RtlFormatCurrentUserKeyPath(ptr) @ stdcall RtlFormatMessage(ptr long long long long ptr ptr long) @ stdcall RtlFreeAnsiString(long) -@ stub RtlFreeHandle +@ stdcall RtlFreeHandle(ptr ptr) @ stdcall RtlFreeHeap(long long long) @ stdcall RtlFreeOemString(ptr) @ stdcall RtlFreeSid (long) @@ -474,6 +476,7 @@ @ stdcall RtlInitializeCriticalSection(ptr) @ stdcall RtlInitializeCriticalSectionAndSpinCount(ptr long) @ stdcall RtlInitializeGenericTable(ptr ptr ptr ptr ptr) +@ stdcall RtlInitializeHandleTable(long long ptr) @ stub RtlInitializeRXact @ stdcall RtlInitializeResource(ptr) @ stdcall RtlInitializeSid(ptr ptr long) @@ -485,6 +488,8 @@ @ stub RtlIsGenericTableEmpty @ stdcall RtlIsNameLegalDOS8Dot3(ptr ptr ptr) @ stdcall RtlIsTextUnicode(ptr long ptr) +@ stdcall RtlIsValidHandle(ptr ptr) +@ stdcall RtlIsValidIndexHandle(ptr long ptr) @ stdcall -ret64 RtlLargeIntegerAdd(long long long long) @ stdcall -ret64 RtlLargeIntegerArithmeticShift(long long long) @ stdcall -ret64 RtlLargeIntegerDivide(long long long long ptr) @@ -1081,10 +1086,7 @@ @ stub NtSignalAndWaitForSingleObject @ stub NtWriteFileGather @ stub RtlAddAtomToAtomTable -@ stub RtlAllocateHandle @ stub RtlCreateAtomTable -@ stub RtlInitializeHandleTable -@ stub RtlIsValidHandle @ stub RtlLookupAtomInAtomTable @ stdcall RtlTryEnterCriticalSection(ptr) @ stub RtlEnumerateProperties diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c index 8de7132e0db..e7f19b7d5c8 100644 --- a/dlls/ntdll/tests/rtl.c +++ b/dlls/ntdll/tests/rtl.c @@ -40,7 +40,11 @@ static ULONG (WINAPI *pRtlRandom)(PULONG); static BOOLEAN (WINAPI *pRtlAreAllAccessesGranted)(ACCESS_MASK, ACCESS_MASK); static BOOLEAN (WINAPI *pRtlAreAnyAccessesGranted)(ACCESS_MASK, ACCESS_MASK); static DWORD (WINAPI *pRtlComputeCrc32)(DWORD,const BYTE*,INT); - +static void (WINAPI * pRtlInitializeHandleTable)(ULONG, ULONG, RTL_HANDLE_TABLE *); +static BOOLEAN (WINAPI * pRtlIsValidIndexHandle)(const RTL_HANDLE_TABLE *, ULONG, RTL_HANDLE **); +static NTSTATUS (WINAPI * pRtlDestroyHandleTable)(RTL_HANDLE_TABLE *); +static RTL_HANDLE * (WINAPI * pRtlAllocateHandle)(RTL_HANDLE_TABLE *, ULONG *); +static BOOLEAN (WINAPI * pRtlFreeHandle)(RTL_HANDLE_TABLE *, RTL_HANDLE *); #define LEN 16 static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */ static ULONG src_aligned_block[4]; @@ -65,6 +69,11 @@ static void InitFunctionPtrs(void) pRtlAreAllAccessesGranted = (void *)GetProcAddress(hntdll, "RtlAreAllAccessesGranted"); pRtlAreAnyAccessesGranted = (void *)GetProcAddress(hntdll, "RtlAreAnyAccessesGranted"); pRtlComputeCrc32 = (void *)GetProcAddress(hntdll, "RtlComputeCrc32"); + pRtlInitializeHandleTable = (void *)GetProcAddress(hntdll, "RtlInitializeHandleTable"); + pRtlIsValidIndexHandle = (void *)GetProcAddress(hntdll, "RtlIsValidIndexHandle"); + pRtlDestroyHandleTable = (void *)GetProcAddress(hntdll, "RtlDestroyHandleTable"); + pRtlAllocateHandle = (void *)GetProcAddress(hntdll, "RtlAllocateHandle"); + pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle"); } strcpy((char*)src_aligned_block, src_src); ok(strlen(src) == 15, "Source must be 16 bytes long!\n"); @@ -816,6 +825,40 @@ static void test_RtlComputeCrc32() ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8lx\n", crc); } + +typedef struct MY_HANDLE +{ + RTL_HANDLE RtlHandle; + void * MyValue; +} MY_HANDLE; + +static inline void RtlpMakeHandleAllocated(RTL_HANDLE * Handle) +{ + ULONG_PTR *AllocatedBit = (ULONG_PTR *)(&Handle->Next); + *AllocatedBit = *AllocatedBit | 1; +} + +static void test_HandleTables() +{ + BOOLEAN result; + NTSTATUS status; + ULONG Index; + MY_HANDLE * MyHandle; + RTL_HANDLE_TABLE HandleTable; + + pRtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable); + MyHandle = (MY_HANDLE *)pRtlAllocateHandle(&HandleTable, &Index); + ok(MyHandle != NULL, "RtlAllocateHandle failed\n"); + RtlpMakeHandleAllocated(&MyHandle->RtlHandle); + MyHandle = NULL; + result = pRtlIsValidIndexHandle(&HandleTable, Index, (RTL_HANDLE **)&MyHandle); + ok(result, "Handle %p wasn't valid\n", MyHandle); + result = pRtlFreeHandle(&HandleTable, &MyHandle->RtlHandle); + ok(result, "Couldn't free handle %p\n", MyHandle); + status = pRtlDestroyHandleTable(&HandleTable); + ok(status == STATUS_SUCCESS, "RtlDestroyHandleTable failed with error 0x%08lx\n", status); +} + START_TEST(rtl) { InitFunctionPtrs(); @@ -844,4 +887,6 @@ START_TEST(rtl) test_RtlAreAnyAccessesGranted(); if (pRtlComputeCrc32) test_RtlComputeCrc32(); + if (pRtlInitializeHandleTable) + test_HandleTables(); } diff --git a/include/winternl.h b/include/winternl.h index e65d929bada..8a58bedc74e 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1166,6 +1166,24 @@ typedef struct _PORT_MESSAGE_HEADER { ULONG SectionSize; } PORT_MESSAGE_HEADER, *PPORT_MESSAGE_HEADER, PORT_MESSAGE, *PPORT_MESSAGE; +/* FIXME: names probably not correct */ +typedef struct _RTL_HANDLE +{ + struct _RTL_HANDLE * Next; +} RTL_HANDLE; + +/* FIXME: names probably not correct */ +typedef struct _RTL_HANDLE_TABLE +{ + ULONG MaxHandleCount; /* 0x00 */ + ULONG HandleSize; /* 0x04 */ + ULONG Unused[2]; /* 0x08-0x0c */ + PVOID NextFree; /* 0x10 */ + PVOID FirstHandle; /* 0x14 */ + PVOID ReservedMemory; /* 0x18 */ + PVOID MaxHandle; /* 0x1c */ +} RTL_HANDLE_TABLE; + /*********************************************************************** * Defines */ @@ -1436,6 +1454,7 @@ NTSTATUS WINAPI RtlAddAccessDeniedAceEx(PACL,DWORD,DWORD,DWORD,PSID); PVOID WINAPI RtlAddVectoredExceptionHandler(ULONG,PVECTORED_EXCEPTION_HANDLER); DWORD WINAPI RtlAdjustPrivilege(DWORD,DWORD,DWORD,DWORD); BOOLEAN WINAPI RtlAllocateAndInitializeSid(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID *); +RTL_HANDLE * WINAPI RtlAllocateHandle(RTL_HANDLE_TABLE *,ULONG *); PVOID WINAPI RtlAllocateHeap(HANDLE,ULONG,ULONG); DWORD WINAPI RtlAnsiStringToUnicodeSize(const STRING *); NTSTATUS WINAPI RtlAnsiStringToUnicodeString(PUNICODE_STRING,PCANSI_STRING,BOOLEAN); @@ -1482,6 +1501,7 @@ void WINAPI RtlDeleteResource(LPRTL_RWLOCK); DWORD WINAPI RtlDeleteSecurityObject(DWORD); PRTL_USER_PROCESS_PARAMETERS WINAPI RtlDeNormalizeProcessParams(RTL_USER_PROCESS_PARAMETERS*); NTSTATUS WINAPI RtlDestroyEnvironment(PWSTR); +NTSTATUS WINAPI RtlDestroyHandleTable(RTL_HANDLE_TABLE *); HANDLE WINAPI RtlDestroyHeap(HANDLE); void WINAPI RtlDestroyProcessParameters(RTL_USER_PROCESS_PARAMETERS*); DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U(PCWSTR); @@ -1530,6 +1550,7 @@ BOOLEAN WINAPI RtlFirstFreeAce(PACL,PACE_HEADER *); NTSTATUS WINAPI RtlFormatCurrentUserKeyPath(PUNICODE_STRING); NTSTATUS WINAPI RtlFormatMessage(LPWSTR,UCHAR,BOOLEAN,BOOLEAN,BOOLEAN,va_list *,LPWSTR,ULONG); void WINAPI RtlFreeAnsiString(PANSI_STRING); +BOOLEAN WINAPI RtlFreeHandle(RTL_HANDLE_TABLE *,RTL_HANDLE *); BOOLEAN WINAPI RtlFreeHeap(HANDLE,ULONG,PVOID); void WINAPI RtlFreeOemString(POEM_STRING); DWORD WINAPI RtlFreeSid(PSID); @@ -1562,6 +1583,7 @@ NTSTATUS WINAPI RtlInitUnicodeStringEx(PUNICODE_STRING,PCWSTR); NTSTATUS WINAPI RtlInitializeCriticalSection(RTL_CRITICAL_SECTION *); NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount(RTL_CRITICAL_SECTION *,DWORD); void WINAPI RtlInitializeBitMap(PRTL_BITMAP,PULONG,ULONG); +void WINAPI RtlInitializeHandleTable(ULONG,ULONG,RTL_HANDLE_TABLE *); void WINAPI RtlInitializeResource(LPRTL_RWLOCK); BOOL WINAPI RtlInitializeSid(PSID,PSID_IDENTIFIER_AUTHORITY,BYTE); @@ -1571,6 +1593,8 @@ NTSTATUS WINAPI RtlIntegerToUnicodeString(ULONG,ULONG,UNICODE_STRING *); ULONG WINAPI RtlIsDosDeviceName_U(PCWSTR); BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3(const UNICODE_STRING*,POEM_STRING,PBOOLEAN); DWORD WINAPI RtlIsTextUnicode(LPVOID,DWORD,DWORD *); +BOOLEAN WINAPI RtlIsValidHandle(const RTL_HANDLE_TABLE *, const RTL_HANDLE *); +BOOLEAN WINAPI RtlIsValidIndexHandle(const RTL_HANDLE_TABLE *, ULONG Index, RTL_HANDLE **); LONGLONG WINAPI RtlLargeIntegerAdd(LONGLONG,LONGLONG); LONGLONG WINAPI RtlLargeIntegerArithmeticShift(LONGLONG,INT); -- 2.11.4.GIT