9d5b06f68e0397d0708f4df6a08b4c7dfebd1dae
[reactos.git] / reactos / subsys / win32k / ntuser / object.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: User object manager
24 * FILE: subsys/win32k/misc/object.c
25 * PROGRAMMERS: David Welch (welch@cwcom.net)
26 * Casper S. Hornstrup (chorns@users.sourceforge.net)
27 * UPDATE HISTORY:
28 * 06-06-2001 CSH Ported kernel object manager
29 */
30 /* INCLUDES ******************************************************************/
31
32 #include <w32k.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 #define USER_HEADER_TO_BODY(ObjectHeader) \
38 ((PVOID)(((PUSER_OBJECT_HEADER)ObjectHeader) + 1))
39
40 #define USER_BODY_TO_HEADER(ObjectBody) \
41 ((PUSER_OBJECT_HEADER)(((PUSER_OBJECT_HEADER)ObjectBody) - 1))
42
43
44 PUSER_HANDLE_TABLE gHandleTable = NULL;
45
46 /* FUNCTIONS *****************************************************************/
47
48 VOID FASTCALL
49 ObmpPerformRetentionChecks(PUSER_OBJECT_HEADER ObjectHeader)
50 {
51 if (ObjectHeader->RefCount < 0)
52 {
53 DPRINT1("ObjectHeader 0x%X has invalid reference count (%d)\n",
54 ObjectHeader, ObjectHeader->RefCount);
55 }
56
57 if (ObjectHeader->HandleCount < 0)
58 {
59 DPRINT1("Object 0x%X has invalid handle count (%d)\n",
60 ObjectHeader, ObjectHeader->HandleCount);
61 }
62
63 if ((ObjectHeader->RefCount == 0) && (ObjectHeader->HandleCount == 0))
64 {
65 ExFreePool(ObjectHeader);
66 }
67 }
68
69 PUSER_HANDLE FASTCALL
70 ObmpGetObjectByHandle(PUSER_HANDLE_TABLE HandleTable,
71 HANDLE Handle)
72 /*
73 * FUNCTION: Get the data structure for a handle
74 * ARGUMENTS:
75 * HandleTable = Table to search
76 * Handle = Handle to get data structure for
77 * RETURNS:
78 * Pointer to the data structure identified by the handle on success,
79 * NULL on failure
80 */
81 {
82 ULONG Index = (((ULONG)Handle) >> 2) - 1;
83 ULONG Count = Index / HANDLE_BLOCK_ENTRIES;
84 PUSER_HANDLE_BLOCK Block = NULL;
85 PLIST_ENTRY Current;
86 ULONG i;
87
88 if (NULL == Handle)
89 {
90 return NULL;
91 }
92
93 Current = HandleTable->ListHead.Flink;
94
95 for (i = 0; i < Count; i++)
96 {
97 Current = Current->Flink;
98 if (Current == &(HandleTable->ListHead))
99 {
100 DPRINT1("Invalid handle 0x%x\n", Handle);
101 return NULL;
102 }
103 }
104
105 Block = CONTAINING_RECORD(Current, USER_HANDLE_BLOCK, ListEntry);
106 return &(Block->Handles[Index % HANDLE_BLOCK_ENTRIES]);
107 }
108
109 VOID FASTCALL
110 ObmpCloseAllHandles(PUSER_HANDLE_TABLE HandleTable)
111 {
112 PLIST_ENTRY CurrentEntry;
113 PUSER_HANDLE_BLOCK Current;
114 PVOID ObjectBody;
115 ULONG i;
116
117 CurrentEntry = HandleTable->ListHead.Flink;
118
119 while (CurrentEntry != &HandleTable->ListHead)
120 {
121 Current = CONTAINING_RECORD(CurrentEntry, USER_HANDLE_BLOCK, ListEntry);
122
123 for (i = 0; i < HANDLE_BLOCK_ENTRIES; i++)
124 {
125 ObjectBody = Current->Handles[i].ObjectBody;
126
127 if (ObjectBody != NULL)
128 {
129 PUSER_OBJECT_HEADER ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
130
131 ObmReferenceObjectByPointer(ObjectBody, otUnknown);
132 ObjectHeader->HandleCount--;
133 Current->Handles[i].ObjectBody = NULL;
134
135 ObmDereferenceObject(ObjectBody);
136
137 CurrentEntry = &HandleTable->ListHead;
138 break;
139 }
140 }
141
142 CurrentEntry = CurrentEntry->Flink;
143 }
144
145 }
146
147 VOID FASTCALL
148 ObmpDeleteHandleTable(PUSER_HANDLE_TABLE HandleTable)
149 {
150 PUSER_HANDLE_BLOCK Current;
151 PLIST_ENTRY CurrentEntry;
152
153 ObmpCloseAllHandles(HandleTable);
154
155 CurrentEntry = RemoveHeadList(&HandleTable->ListHead);
156
157 while (CurrentEntry != &HandleTable->ListHead)
158 {
159 Current = CONTAINING_RECORD(CurrentEntry,
160 USER_HANDLE_BLOCK,
161 ListEntry);
162
163 ExFreePool(Current);
164
165 CurrentEntry = RemoveHeadList(&HandleTable->ListHead);
166 }
167 }
168
169 PVOID FASTCALL
170 ObmpDeleteHandle(PUSER_HANDLE_TABLE HandleTable,
171 HANDLE Handle)
172 {
173 PUSER_OBJECT_HEADER ObjectHeader;
174 PUSER_HANDLE Entry;
175 PVOID ObjectBody;
176
177 Entry = ObmpGetObjectByHandle(HandleTable, Handle);
178 if (Entry == NULL)
179 {
180 DPRINT1("Invalid handle\n");
181 return NULL;
182 }
183
184 ObjectBody = Entry->ObjectBody;
185
186 if (ObjectBody != NULL)
187 {
188 ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
189 ObjectHeader->HandleCount--;
190 ObmReferenceObjectByPointer(ObjectBody, otUnknown);
191 Entry->ObjectBody = NULL;
192 }
193
194 return ObjectBody;
195 }
196
197 NTSTATUS FASTCALL
198 ObmpInitializeObject(PUSER_HANDLE_TABLE HandleTable,
199 PUSER_OBJECT_HEADER ObjectHeader,
200 PHANDLE Handle,
201 USER_OBJECT_TYPE ObjectType,
202 ULONG ObjectSize)
203 {
204 DWORD Status = STATUS_SUCCESS;
205
206 ObjectHeader->Type = ObjectType;
207 ObjectHeader->HandleCount = 0;
208 ObjectHeader->RefCount = 1;
209 ObjectHeader->Size = ObjectSize;
210
211 if (Handle != NULL)
212 {
213 Status = ObmCreateHandle(HandleTable,
214 USER_HEADER_TO_BODY(ObjectHeader),
215 Handle);
216 }
217
218 return Status;
219 }
220
221
222 ULONG FASTCALL
223 ObmGetReferenceCount(PVOID ObjectBody)
224 {
225 PUSER_OBJECT_HEADER ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
226
227 return ObjectHeader->RefCount;
228 }
229
230 ULONG FASTCALL
231 ObmGetHandleCount(PVOID ObjectBody)
232 {
233 PUSER_OBJECT_HEADER ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
234
235 return ObjectHeader->HandleCount;
236 }
237
238 VOID FASTCALL
239 ObmReferenceObject(PVOID ObjectBody)
240 /*
241 * FUNCTION: Increments a given object's reference count and performs
242 * retention checks
243 * ARGUMENTS:
244 * ObjectBody = Body of the object
245 */
246 {
247 PUSER_OBJECT_HEADER ObjectHeader;
248
249 if (!ObjectBody)
250 {
251 DPRINT1("Cannot Reference NULL!\n");
252 return;
253 }
254
255 ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
256
257 ObjectHeader->RefCount++;
258
259 ObmpPerformRetentionChecks(ObjectHeader);
260 }
261
262 VOID FASTCALL
263 ObmDereferenceObject(PVOID ObjectBody)
264 /*
265 * FUNCTION: Decrements a given object's reference count and performs
266 * retention checks
267 * ARGUMENTS:
268 * ObjectBody = Body of the object
269 */
270 {
271 PUSER_OBJECT_HEADER ObjectHeader;
272
273 if (!ObjectBody)
274 {
275 DPRINT1("Cannot Dereference NULL!\n");
276 return;
277 }
278
279 ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
280
281 ObjectHeader->RefCount--;
282 ObmpPerformRetentionChecks(ObjectHeader);
283 }
284
285 NTSTATUS FASTCALL
286 ObmReferenceObjectByPointer(PVOID ObjectBody,
287 USER_OBJECT_TYPE ObjectType)
288 /*
289 * FUNCTION: Increments the pointer reference count for a given object
290 * ARGUMENTS:
291 * ObjectBody = Object's body
292 * ObjectType = Object type
293 * RETURNS: Status
294 */
295 {
296 PUSER_OBJECT_HEADER ObjectHeader;
297
298 ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
299
300 if ((ObjectType != otUnknown) && (ObjectHeader->Type != ObjectType))
301 {
302 return STATUS_INVALID_PARAMETER;
303 }
304 ObjectHeader->RefCount++;
305
306 return STATUS_SUCCESS;
307 }
308
309 PVOID FASTCALL
310 ObmCreateObject(PUSER_HANDLE_TABLE HandleTable,
311 PHANDLE Handle,
312 USER_OBJECT_TYPE ObjectType,
313 ULONG ObjectSize)
314 {
315 PUSER_OBJECT_HEADER ObjectHeader;
316 PVOID ObjectBody;
317 DWORD Status;
318
319 ObjectHeader = (PUSER_OBJECT_HEADER)ExAllocatePool(PagedPool,
320 ObjectSize + sizeof(USER_OBJECT_HEADER));
321 if (!ObjectHeader)
322 {
323 return NULL;
324 }
325
326 ObjectBody = USER_HEADER_TO_BODY(ObjectHeader);
327
328 RtlZeroMemory(ObjectBody, ObjectSize);
329
330 Status = ObmpInitializeObject(HandleTable,
331 ObjectHeader,
332 Handle,
333 ObjectType,
334 ObjectSize);
335
336 if (!NT_SUCCESS(Status))
337 {
338 ExFreePool(ObjectHeader);
339 return NULL;
340 }
341
342 return ObjectBody;
343 }
344
345 NTSTATUS FASTCALL
346 ObmCreateHandle(PUSER_HANDLE_TABLE HandleTable,
347 PVOID ObjectBody,
348 PHANDLE HandleReturn)
349 /*
350 * FUNCTION: Add a handle referencing an object
351 * ARGUMENTS:
352 * HandleTable = Table to put handle in
353 * ObjectBody = Object body that the handle should refer to
354 * RETURNS: The created handle
355 */
356 {
357 PUSER_HANDLE_BLOCK NewBlock;
358 PLIST_ENTRY Current;
359 ULONG Handle;
360 ULONG i;
361
362 if (ObjectBody != NULL)
363 {
364 USER_BODY_TO_HEADER(ObjectBody)->HandleCount++;
365 }
366
367 Handle = 1;
368 Current = HandleTable->ListHead.Flink;
369 /*
370 * Scan through the currently allocated Handle blocks looking for a free
371 * slot
372 */
373 while (Current != &(HandleTable->ListHead))
374 {
375 PUSER_HANDLE_BLOCK Block =
376 CONTAINING_RECORD(Current, USER_HANDLE_BLOCK, ListEntry);
377
378 for (i = 0; i < HANDLE_BLOCK_ENTRIES; i++)
379 {
380 if (!Block->Handles[i].ObjectBody)
381 {
382 Block->Handles[i].ObjectBody = ObjectBody;
383 *HandleReturn = (HANDLE)((Handle + i) << 2);
384 return STATUS_SUCCESS;
385 }
386 }
387
388 Handle = Handle + HANDLE_BLOCK_ENTRIES;
389 Current = Current->Flink;
390 }
391
392 /*
393 * Add a new Handle block to the end of the list
394 */
395 NewBlock = (PUSER_HANDLE_BLOCK)ExAllocatePool(PagedPool,
396 sizeof(USER_HANDLE_BLOCK));
397 if (!NewBlock)
398 {
399 DPRINT1("Unable to allocate new handle block\n");
400 *HandleReturn = (PHANDLE)NULL;
401 return STATUS_INSUFFICIENT_RESOURCES;
402 }
403
404 RtlZeroMemory(NewBlock, sizeof(USER_HANDLE_BLOCK));
405 NewBlock->Handles[0].ObjectBody = ObjectBody;
406 InsertTailList(&HandleTable->ListHead, &NewBlock->ListEntry);
407 *HandleReturn = (HANDLE)(Handle << 2);
408
409 return STATUS_SUCCESS;
410 }
411
412 NTSTATUS FASTCALL
413 ObmReferenceObjectByHandle(PUSER_HANDLE_TABLE HandleTable,
414 HANDLE Handle,
415 USER_OBJECT_TYPE ObjectType,
416 PVOID* Object)
417 /*
418 * FUNCTION: Increments the reference count for an object and returns a
419 * pointer to its body
420 * ARGUMENTS:
421 * HandleTable = Table to search
422 * Handle = Handle for the object
423 * ObjectType = Type of object
424 * Object (OUT) = Points to the object body on return
425 * RETURNS: Status
426 */
427 {
428 PUSER_OBJECT_HEADER ObjectHeader;
429 PUSER_HANDLE UserHandle;
430 PVOID ObjectBody;
431
432 UserHandle = ObmpGetObjectByHandle(HandleTable, Handle);
433
434 if ((UserHandle == NULL) || (UserHandle->ObjectBody == NULL))
435 {
436 return STATUS_UNSUCCESSFUL;
437 }
438
439 ObjectBody = UserHandle->ObjectBody;
440 ObmReferenceObjectByPointer(ObjectBody, ObjectType);
441
442 ObjectHeader = USER_BODY_TO_HEADER(ObjectBody);
443
444 if ((ObjectType != otUnknown) && (ObjectHeader->Type != ObjectType))
445 {
446 DPRINT1("Object type mismatch 0x%x 0x%x\n", ObjectType, ObjectHeader->Type);
447 ObmDereferenceObject(ObjectBody);
448 return STATUS_UNSUCCESSFUL;
449 }
450
451 *Object = ObjectBody;
452
453 return STATUS_SUCCESS;
454 }
455
456 NTSTATUS FASTCALL
457 ObmCloseHandle(PUSER_HANDLE_TABLE HandleTable,
458 HANDLE Handle)
459 {
460 PVOID ObjectBody;
461
462 ObjectBody = ObmpDeleteHandle(HandleTable, Handle);
463 if (ObjectBody == NULL)
464 {
465 return STATUS_UNSUCCESSFUL;
466 }
467
468 ObmDereferenceObject(ObjectBody);
469
470 return STATUS_SUCCESS;
471 }
472
473 VOID FASTCALL
474 ObmInitializeHandleTable(PUSER_HANDLE_TABLE HandleTable)
475 {
476 InitializeListHead(&HandleTable->ListHead);
477 }
478
479 VOID FASTCALL
480 ObmFreeHandleTable(PUSER_HANDLE_TABLE HandleTable)
481 {
482 ObmpDeleteHandleTable(HandleTable);
483 }
484
485 PUSER_HANDLE_TABLE FASTCALL
486 ObmCreateHandleTable(VOID)
487 {
488 PUSER_HANDLE_TABLE HandleTable;
489
490 HandleTable = (PUSER_HANDLE_TABLE)ExAllocatePool(PagedPool,
491 sizeof(USER_HANDLE_TABLE));
492 if (!HandleTable)
493 {
494 DPRINT1("Unable to create handle table\n");
495 return NULL;
496 }
497
498 ObmInitializeHandleTable(HandleTable);
499
500 return HandleTable;
501 }
502
503 VOID FASTCALL
504 ObmDestroyHandleTable(PUSER_HANDLE_TABLE HandleTable)
505 {
506 ObmFreeHandleTable(HandleTable);
507 ExFreePool(HandleTable);
508 }
509
510 /* EOF */