- Always query for the correct stub interface, otherwise we will be
[wine] / dlls / ntdll / handletable.c
1 /*
2  * Handle Tables
3  *
4  * Copyright (C) 2004 Robert Shearman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winternl.h"
25 #include "wine/debug.h"
26 #include "ntdll_misc.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
29
30 /**************************************************************************
31  *      RtlInitializeHandleTable   (NTDLL.@)
32  *
33  * Initializes a handle table.
34  *
35  * PARAMS
36  *  MaxHandleCount [I] The maximum number of handles the handle table will support.
37  *  HandleSize     [I] The size of each handle.
38  *  HandleTable    [I/O] The handle table.
39  *
40  * RETURNS
41  *  Nothing.
42  *
43  * SEE
44  *  RtlDestroyHandleTable().
45  */
46 void WINAPI RtlInitializeHandleTable(ULONG MaxHandleCount, ULONG HandleSize, RTL_HANDLE_TABLE * HandleTable)
47 {
48     TRACE("(%lu, %lu, %p)\n", MaxHandleCount, HandleSize, HandleTable);
49
50     memset(HandleTable, 0, sizeof(*HandleTable));
51     HandleTable->MaxHandleCount = MaxHandleCount;
52     HandleTable->HandleSize = HandleSize;
53 }
54
55 /**************************************************************************
56  *      RtlDestroyHandleTable   (NTDLL.@)
57  *
58  * Destroys a handle table and frees associated resources.
59  *
60  * PARAMS
61  *  HandleTable    [I] The handle table.
62  *
63  * RETURNS
64  *  Any status code returned by NtFreeVirtualMemory().
65  *
66  * NOTES
67  *  The native version of this API doesn't free the virtual memory that has
68  *  been previously reserved, only the committed memory. There is no harm
69  *  in also freeing the reserved memory because it won't have been handed out
70  *  to any callers. I believe it is "more polite" to free everything.
71  *
72  * SEE
73  *  RtlInitializeHandleTable().
74  */
75 NTSTATUS WINAPI RtlDestroyHandleTable(RTL_HANDLE_TABLE * HandleTable)
76 {
77     ULONG Size = 0;
78
79     TRACE("(%p)\n", HandleTable);
80
81     /* native version only releases committed memory, but we also release reserved */
82     return NtFreeVirtualMemory(
83         NtCurrentProcess(),
84         &HandleTable->FirstHandle,
85         &Size,
86         MEM_RELEASE);
87 }
88
89 /**************************************************************************
90  *      RtlpAllocateSomeHandles   (internal)
91  *
92  * Reserves memory for the handles if not previously done and commits memory
93  * for a batch of handles if none are free and adds them to the free list.
94  *
95  * PARAMS
96  *  HandleTable    [I/O] The handle table.
97  *
98  * RETURNS
99  *  NTSTATUS code.
100  */
101 static NTSTATUS RtlpAllocateSomeHandles(RTL_HANDLE_TABLE * HandleTable)
102 {
103     NTSTATUS status;
104     if (!HandleTable->FirstHandle)
105     {
106         PVOID FirstHandleAddr = NULL;
107         ULONG MaxSize = HandleTable->MaxHandleCount * HandleTable->HandleSize;
108
109         /* reserve memory for the handles, but don't commit it yet because we
110          * probably won't use most of it and it will use up physical memory */
111         status = NtAllocateVirtualMemory(
112             NtCurrentProcess(),
113             &FirstHandleAddr,
114             0,
115             &MaxSize,
116             MEM_RESERVE,
117             PAGE_READWRITE);
118         if (status != STATUS_SUCCESS)
119             return status;
120         HandleTable->FirstHandle = FirstHandleAddr;
121         HandleTable->ReservedMemory = HandleTable->FirstHandle;
122         HandleTable->MaxHandle = (char *)HandleTable->FirstHandle + MaxSize;
123     }
124     if (!HandleTable->NextFree)
125     {
126         ULONG CommitSize = 4096; /* one page */
127         ULONG Offset;
128         RTL_HANDLE * FreeHandle = NULL;
129         PVOID NextAvailAddr = HandleTable->ReservedMemory;
130
131         if (HandleTable->ReservedMemory >= HandleTable->MaxHandle)
132             return STATUS_NO_MEMORY; /* the handle table is completely full */
133
134         status = NtAllocateVirtualMemory(
135             NtCurrentProcess(),
136             &NextAvailAddr,
137             0,
138             &CommitSize,
139             MEM_COMMIT,
140             PAGE_READWRITE);
141         if (status != STATUS_SUCCESS)
142             return status;
143
144         for (Offset = 0; Offset < CommitSize; Offset += HandleTable->HandleSize)
145         {
146             /* make sure we don't go over handle limit, even if we can
147              * because of rounding of the table size up to the next page
148              * boundary */
149             if ((char *)HandleTable->ReservedMemory + Offset >= (char *)HandleTable->MaxHandle)
150                 break;
151
152             FreeHandle = (RTL_HANDLE *)((char *)HandleTable->ReservedMemory + Offset);
153
154             FreeHandle->Next = (RTL_HANDLE *)((char *)HandleTable->ReservedMemory + 
155                 Offset + HandleTable->HandleSize);
156         }
157
158         /* shouldn't happen because we already test for this above, but
159          * handle it just in case */
160         if (!FreeHandle)
161             return STATUS_NO_MEMORY;
162
163         /* set the last handle's Next pointer to NULL so that when we run
164          * out of free handles we trigger another commit of memory and
165          * initialize the free pointers */
166         FreeHandle->Next = NULL;
167
168         HandleTable->NextFree = HandleTable->ReservedMemory;
169
170         HandleTable->ReservedMemory = (char *)HandleTable->ReservedMemory + CommitSize;
171     }
172     return STATUS_SUCCESS;
173 }
174
175 /**************************************************************************
176  *      RtlAllocateHandle   (NTDLL.@)
177  *
178  * Allocates a handle from the handle table.
179  *
180  * PARAMS
181  *  HandleTable    [I/O] The handle table.
182  *  HandleIndex    [O] Index of the handle returned. Optional.
183  *
184  * RETURNS
185  *  Success: Pointer to allocated handle.
186  *  Failure: NULL.
187  *
188  * SEE
189  *  RtlFreeHandle().
190  */
191 RTL_HANDLE * WINAPI RtlAllocateHandle(RTL_HANDLE_TABLE * HandleTable, ULONG * HandleIndex)
192 {
193     RTL_HANDLE * ret;
194
195     TRACE("(%p, %p)\n", HandleTable, HandleIndex);
196
197     if (!HandleTable->NextFree && RtlpAllocateSomeHandles(HandleTable) != STATUS_SUCCESS)
198         return NULL;
199     
200     ret = (RTL_HANDLE *)HandleTable->NextFree;
201     HandleTable->NextFree = ret->Next;
202
203     if (HandleIndex)
204         *HandleIndex = (ULONG)(((PCHAR)ret - (PCHAR)HandleTable->FirstHandle) / HandleTable->HandleSize);
205
206     return ret;
207 }
208
209 /**************************************************************************
210  *      RtlFreeHandle   (NTDLL.@)
211  *
212  * Frees an allocated handle.
213  *
214  * PARAMS
215  *  HandleTable    [I/O] The handle table.
216  *  Handle         [I] The handle to be freed.
217  *
218  * RETURNS
219  *  Success: TRUE.
220  *  Failure: FALSE.
221  *
222  * SEE
223  *  RtlAllocateHandle().
224  */
225 BOOLEAN WINAPI RtlFreeHandle(RTL_HANDLE_TABLE * HandleTable, RTL_HANDLE * Handle)
226 {
227     TRACE("(%p, %p)\n", HandleTable, Handle);
228     /* NOTE: we don't validate the handle and we don't make Handle->Next even
229      * again to signal that it is no longer in user - that is done as a side
230      * effect of setting Handle->Next to the previously next free handle in
231      * the handle table */
232     memset(Handle, 0, HandleTable->HandleSize);
233     Handle->Next = (RTL_HANDLE *)HandleTable->NextFree;
234     HandleTable->NextFree = Handle;
235     return TRUE;
236 }
237
238 /**************************************************************************
239  *      RtlIsValidHandle   (NTDLL.@)
240  *
241  * Determines whether a handle is valid or not.
242  *
243  * PARAMS
244  *  HandleTable    [I] The handle table.
245  *  Handle         [I] The handle to be tested.
246  *
247  * RETURNS
248  *  Valid: TRUE.
249  *  Invalid: FALSE.
250  */
251 BOOLEAN WINAPI RtlIsValidHandle(const RTL_HANDLE_TABLE * HandleTable, const RTL_HANDLE * Handle)
252 {
253     TRACE("(%p, %p)\n", HandleTable, Handle);
254     /* make sure handle is within used region and that it is aligned on
255      * a HandleTable->HandleSize boundary and that Handle->Next is odd,
256      * indicating that the handle is active */
257     if ((Handle >= (RTL_HANDLE *)HandleTable->FirstHandle) &&
258       (Handle < (RTL_HANDLE *)HandleTable->ReservedMemory) &&
259       !((ULONG_PTR)Handle & (HandleTable->HandleSize - 1)) &&
260       ((ULONG_PTR)Handle->Next & 1))
261         return TRUE;
262     else
263         return FALSE;
264 }
265
266 /**************************************************************************
267  *      RtlIsValidIndexHandle   (NTDLL.@)
268  *
269  * Determines whether a handle index is valid or not.
270  *
271  * PARAMS
272  *  HandleTable    [I] The handle table.
273  *  Index          [I] The index of the handle to be tested.
274  *  ValidHandle    [O] The handle Index refers to.
275  *
276  * RETURNS
277  *  Valid: TRUE.
278  *  Invalid: FALSE.
279  */
280 BOOLEAN WINAPI RtlIsValidIndexHandle(const RTL_HANDLE_TABLE * HandleTable, ULONG Index, RTL_HANDLE ** ValidHandle)
281 {
282     RTL_HANDLE * Handle;
283
284     TRACE("(%p, %lu, %p)\n", HandleTable, Index, ValidHandle);
285     Handle = (RTL_HANDLE *)
286         ((char *)HandleTable->FirstHandle + Index * HandleTable->HandleSize);
287
288     if (RtlIsValidHandle(HandleTable, Handle))
289     {
290         *ValidHandle = Handle;
291         return TRUE;
292     }
293     return FALSE;
294 }