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