update for HEAD-2003091401
[reactos.git] / subsys / win32k / misc / 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 <ddk/ntddk.h>
33 #include <include/object.h>
34
35 #define NDEBUG
36 #include <debug.h>
37
38 /* FUNCTIONS *****************************************************************/
39
40 PVOID FASTCALL
41 HEADER_TO_BODY(PUSER_OBJECT_HEADER ObjectHeader)
42 {
43   return (((PUSER_OBJECT_HEADER)ObjectHeader) + 1);
44 }
45
46 PUSER_OBJECT_HEADER FASTCALL
47 BODY_TO_HEADER(PVOID ObjectBody)
48 {
49   return (((PUSER_OBJECT_HEADER)ObjectBody) - 1);
50 }
51
52 VOID STATIC FASTCALL
53 ObmpLockHandleTable(PUSER_HANDLE_TABLE HandleTable)
54 {
55   ExAcquireFastMutex(&HandleTable->ListLock);
56 }
57
58 VOID STATIC FASTCALL
59 ObmpUnlockHandleTable(PUSER_HANDLE_TABLE HandleTable)
60 {
61   ExReleaseFastMutex(&HandleTable->ListLock);
62 }
63
64 VOID FASTCALL
65 ObmpPerformRetentionChecks(PUSER_OBJECT_HEADER ObjectHeader)
66 {
67   if (ObjectHeader->RefCount < 0)
68     {
69       DbgPrint("ObjectHeader 0x%X has invalid reference count (%d)\n",
70                ObjectHeader, ObjectHeader->RefCount);
71     }
72   
73   if (ObjectHeader->HandleCount < 0)
74     {
75       DbgPrint("Object 0x%X has invalid handle count (%d)\n",
76                ObjectHeader, ObjectHeader->HandleCount);
77     }
78   
79   if ((ObjectHeader->RefCount == 0) && (ObjectHeader->HandleCount == 0))
80     {
81       ExFreePool(ObjectHeader);
82     }
83 }
84
85 PUSER_HANDLE FASTCALL
86 ObmpGetObjectByHandle(PUSER_HANDLE_TABLE HandleTable,
87                       HANDLE Handle)
88 /*
89  * FUNCTION: Get the data structure for a handle
90  * ARGUMENTS:
91  *   HandleTable = Table to search
92  *   Handle      = Handle to get data structure for
93  * RETURNS:
94  *   Pointer to the data structure identified by the handle on success,
95  *   NULL on failure
96  */
97 {
98   ULONG Index = (((ULONG)Handle) >> 2) - 1;
99   ULONG Count = Index / HANDLE_BLOCK_ENTRIES;
100   PUSER_HANDLE_BLOCK Block = NULL;
101   PLIST_ENTRY Current;
102   ULONG i;
103   
104   Current = HandleTable->ListHead.Flink;
105   
106   for (i = 0; i < Count; i++)
107     {
108       Current = Current->Flink;
109       if (Current == &(HandleTable->ListHead))
110         {
111           return NULL;
112         }
113     }
114   
115   Block = CONTAINING_RECORD(Current, USER_HANDLE_BLOCK, ListEntry);
116   return &(Block->Handles[Index % HANDLE_BLOCK_ENTRIES]);
117 }
118
119 VOID FASTCALL
120 ObmpCloseAllHandles(PUSER_HANDLE_TABLE HandleTable)
121 {
122   PLIST_ENTRY CurrentEntry;
123   PUSER_HANDLE_BLOCK Current;
124   PVOID ObjectBody;
125   ULONG i;
126
127   ObmpLockHandleTable(HandleTable);
128   
129   CurrentEntry = HandleTable->ListHead.Flink;
130
131   while (CurrentEntry != &HandleTable->ListHead)
132     {
133       Current = CONTAINING_RECORD(CurrentEntry, USER_HANDLE_BLOCK, ListEntry);
134       
135       for (i = 0; i < HANDLE_BLOCK_ENTRIES; i++)
136         {
137           ObjectBody = Current->Handles[i].ObjectBody;
138           
139           if (ObjectBody != NULL)
140             {
141               PUSER_OBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody);
142               
143               ObmReferenceObjectByPointer(ObjectBody, otUnknown);
144               ObjectHeader->HandleCount--;
145               Current->Handles[i].ObjectBody = NULL;
146               
147               ObmpUnlockHandleTable(HandleTable);
148               
149               ObmDereferenceObject(ObjectBody);
150
151               ObmpLockHandleTable(HandleTable);
152               CurrentEntry = &HandleTable->ListHead;
153               break;
154             }
155         }
156       
157       CurrentEntry = CurrentEntry->Flink;
158     }
159   
160   ObmpUnlockHandleTable(HandleTable);
161 }
162
163 VOID FASTCALL
164 ObmpDeleteHandleTable(PUSER_HANDLE_TABLE HandleTable)
165 {
166   PUSER_HANDLE_BLOCK Current;
167   PLIST_ENTRY CurrentEntry;
168
169   ObmpCloseAllHandles(HandleTable);
170   
171   CurrentEntry = RemoveHeadList(&HandleTable->ListHead);
172   
173   while (CurrentEntry != &HandleTable->ListHead)
174   {
175     Current = CONTAINING_RECORD(CurrentEntry,
176                                 USER_HANDLE_BLOCK,
177                                 ListEntry);
178
179     ExFreePool(Current);
180     
181     CurrentEntry = RemoveHeadList(&HandleTable->ListHead);
182   }
183 }
184
185 PVOID FASTCALL
186 ObmpDeleteHandle(PUSER_HANDLE_TABLE HandleTable,
187                  HANDLE Handle)
188 {
189   PUSER_OBJECT_HEADER ObjectHeader;
190   PUSER_HANDLE Entry;
191   PVOID ObjectBody;
192
193   ObmpLockHandleTable(HandleTable);
194   
195   Entry = ObmpGetObjectByHandle(HandleTable, Handle);
196   if (Entry == NULL)
197     {
198       ObmpUnlockHandleTable(HandleTable);
199       return NULL;
200     }
201
202   ObjectBody = Entry->ObjectBody;
203   
204   if (ObjectBody != NULL)
205     {
206       ObjectHeader = BODY_TO_HEADER(ObjectBody);
207       ObjectHeader->HandleCount--;
208       ObmReferenceObjectByPointer(ObjectBody, otUnknown);
209       Entry->ObjectBody = NULL;
210     }
211   
212   ObmpUnlockHandleTable(HandleTable);
213
214   return ObjectBody;
215 }
216
217 NTSTATUS FASTCALL
218 ObmpInitializeObject(PUSER_HANDLE_TABLE HandleTable,
219                      PUSER_OBJECT_HEADER ObjectHeader,
220                      PHANDLE Handle,
221                      USER_OBJECT_TYPE ObjectType,
222                      ULONG ObjectSize)
223 {
224   DWORD Status = STATUS_SUCCESS;
225   
226   ObjectHeader->Type = ObjectType;
227   ObjectHeader->HandleCount = 0;
228   ObjectHeader->RefCount = 1;
229   ObjectHeader->Size = ObjectSize;
230
231   if (Handle != NULL)
232     {
233       Status = ObmCreateHandle(HandleTable,
234                                HEADER_TO_BODY(ObjectHeader),
235                                Handle);
236     }
237
238   return Status;
239 }
240
241
242 ULONG FASTCALL
243 ObmGetReferenceCount(PVOID ObjectBody)
244 {
245   PUSER_OBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody);
246
247   return ObjectHeader->RefCount;
248 }
249
250 ULONG FASTCALL
251 ObmGetHandleCount(PVOID ObjectBody)
252 {
253   PUSER_OBJECT_HEADER ObjectHeader = BODY_TO_HEADER(ObjectBody);
254
255   return ObjectHeader->HandleCount;
256 }
257
258 VOID FASTCALL
259 ObmReferenceObject(PVOID ObjectBody)
260 /*
261  * FUNCTION: Increments a given object's reference count and performs
262  *           retention checks
263  * ARGUMENTS:
264  *   ObjectBody = Body of the object
265  */
266 {
267   PUSER_OBJECT_HEADER ObjectHeader;
268   
269   if (!ObjectBody)
270     {
271       return;
272     }
273   
274   ObjectHeader = BODY_TO_HEADER(ObjectBody);
275   
276   ObjectHeader->RefCount++;
277
278   ObmpPerformRetentionChecks(ObjectHeader);
279 }
280
281 VOID FASTCALL
282 ObmDereferenceObject(PVOID ObjectBody)
283 /*
284  * FUNCTION: Decrements a given object's reference count and performs
285  *           retention checks
286  * ARGUMENTS:
287  *   ObjectBody = Body of the object
288  */
289 {
290   PUSER_OBJECT_HEADER ObjectHeader;
291   
292   if (!ObjectBody)
293     {
294       return;
295     }
296   
297   ObjectHeader = BODY_TO_HEADER(ObjectBody);
298   
299   ObjectHeader->RefCount--;
300
301   ObmpPerformRetentionChecks(ObjectHeader);
302 }
303
304 NTSTATUS FASTCALL
305 ObmReferenceObjectByPointer(PVOID ObjectBody,
306                             USER_OBJECT_TYPE ObjectType)
307 /*
308  * FUNCTION: Increments the pointer reference count for a given object
309  * ARGUMENTS:
310  *         ObjectBody = Object's body
311  *         ObjectType = Object type
312  * RETURNS: Status
313  */
314 {
315   PUSER_OBJECT_HEADER ObjectHeader;
316   
317   ObjectHeader = BODY_TO_HEADER(ObjectBody);
318   
319   if ((ObjectType != otUnknown) && (ObjectHeader->Type != ObjectType))
320     {
321       return STATUS_INVALID_PARAMETER;
322     }
323   
324   ObjectHeader->RefCount++;
325   
326   return STATUS_SUCCESS;
327 }
328
329 PVOID FASTCALL
330 ObmCreateObject(PUSER_HANDLE_TABLE HandleTable,
331                 PHANDLE Handle,
332                 USER_OBJECT_TYPE ObjectType,
333                 ULONG ObjectSize)
334 {
335   PUSER_OBJECT_HEADER ObjectHeader;
336   PVOID ObjectBody;
337   DWORD Status;
338   
339   ObjectHeader = (PUSER_OBJECT_HEADER)ExAllocatePool(NonPagedPool, 
340                                      ObjectSize + sizeof(USER_OBJECT_HEADER));
341   if (!ObjectHeader)
342     {
343       return NULL;
344     }
345   
346   ObjectBody = HEADER_TO_BODY(ObjectHeader);
347   
348   RtlZeroMemory(ObjectBody, ObjectSize);
349   
350   Status = ObmpInitializeObject(HandleTable,
351                                 ObjectHeader,
352                                 Handle,
353                                 ObjectType,
354                                 ObjectSize);
355   
356   if (!NT_SUCCESS(Status))
357     {
358       ExFreePool(ObjectHeader);
359       return NULL;
360     }
361   
362   return ObjectBody;
363 }
364
365 NTSTATUS FASTCALL
366 ObmCreateHandle(PUSER_HANDLE_TABLE HandleTable,
367                 PVOID ObjectBody,
368                 PHANDLE HandleReturn)
369 /*
370  * FUNCTION: Add a handle referencing an object
371  * ARGUMENTS:
372  *   HandleTable = Table to put handle in
373  *   ObjectBody  = Object body that the handle should refer to
374  * RETURNS: The created handle
375  */
376 {
377   PUSER_HANDLE_BLOCK NewBlock;
378   PLIST_ENTRY Current;
379   ULONG Handle;
380   ULONG i;
381
382   if (ObjectBody != NULL) 
383     {
384       BODY_TO_HEADER(ObjectBody)->HandleCount++;
385     }
386   
387   ObmpLockHandleTable(HandleTable);
388
389   Handle = 1;
390   Current = HandleTable->ListHead.Flink;
391   /*
392    * Scan through the currently allocated Handle blocks looking for a free
393    * slot
394    */
395   while (Current != &(HandleTable->ListHead))
396     {
397       PUSER_HANDLE_BLOCK Block = 
398         CONTAINING_RECORD(Current, USER_HANDLE_BLOCK, ListEntry);
399            
400       for (i = 0; i < HANDLE_BLOCK_ENTRIES; i++)
401         {
402           if (!Block->Handles[i].ObjectBody)
403             {
404               Block->Handles[i].ObjectBody = ObjectBody;
405               ObmpUnlockHandleTable(HandleTable);
406               *HandleReturn = (HANDLE)((Handle + i) << 2);
407               return ERROR_SUCCESS;
408             }
409         }
410
411       Handle = Handle + HANDLE_BLOCK_ENTRIES;
412       Current = Current->Flink;
413     }
414
415   /*
416    * Add a new Handle block to the end of the list
417    */
418   NewBlock = (PUSER_HANDLE_BLOCK)ExAllocatePool(NonPagedPool, 
419                                                 sizeof(USER_HANDLE_BLOCK));
420   if (!NewBlock)
421     {
422       *HandleReturn = (PHANDLE)NULL;
423       return STATUS_INSUFFICIENT_RESOURCES;
424     }
425   
426   RtlZeroMemory(NewBlock, sizeof(USER_HANDLE_BLOCK));
427   NewBlock->Handles[0].ObjectBody = ObjectBody;
428   InsertTailList(&HandleTable->ListHead, &NewBlock->ListEntry);
429   ObmpUnlockHandleTable(HandleTable);
430   *HandleReturn = (HANDLE)(Handle << 2);
431
432   return STATUS_SUCCESS;
433 }
434
435 NTSTATUS FASTCALL
436 ObmReferenceObjectByHandle(PUSER_HANDLE_TABLE HandleTable,
437                            HANDLE Handle,
438                            USER_OBJECT_TYPE ObjectType,
439                            PVOID* Object)
440 /*
441  * FUNCTION: Increments the reference count for an object and returns a
442  *           pointer to its body
443  * ARGUMENTS:
444  *         HandleTable = Table to search
445  *         Handle = Handle for the object
446  *         ObjectType = Type of object
447  *         Object (OUT) = Points to the object body on return
448  * RETURNS: Status
449  */
450 {
451   PUSER_OBJECT_HEADER ObjectHeader;
452   PUSER_HANDLE UserHandle;
453   PVOID ObjectBody;
454   
455   ObmpLockHandleTable(HandleTable);
456   
457   UserHandle = ObmpGetObjectByHandle(HandleTable, Handle);
458   
459   if ((UserHandle == NULL) || (UserHandle->ObjectBody == NULL))
460     {
461       ObmpUnlockHandleTable(HandleTable);
462       return STATUS_UNSUCCESSFUL;
463     }
464   
465   ObjectBody = UserHandle->ObjectBody;
466   ObmReferenceObjectByPointer(ObjectBody, ObjectType);
467   
468   ObmpUnlockHandleTable(HandleTable);
469   
470   ObjectHeader = BODY_TO_HEADER(ObjectBody);
471   
472   if ((ObjectType != otUnknown) && (ObjectHeader->Type != ObjectType))
473     {
474       return STATUS_UNSUCCESSFUL;
475     }
476   
477   *Object = ObjectBody;
478   
479   return STATUS_SUCCESS;
480 }
481
482 NTSTATUS FASTCALL
483 ObmCloseHandle(PUSER_HANDLE_TABLE HandleTable,
484                HANDLE Handle)
485 {
486   PVOID ObjectBody;
487
488   ObjectBody = ObmpDeleteHandle(HandleTable, Handle);
489   if (ObjectBody == NULL)
490     {
491       return STATUS_UNSUCCESSFUL;
492     }
493   
494   ObmDereferenceObject(ObjectBody);
495   
496   return STATUS_SUCCESS;
497 }
498
499 VOID FASTCALL
500 ObmInitializeHandleTable(PUSER_HANDLE_TABLE HandleTable)
501 {
502   InitializeListHead(&HandleTable->ListHead);
503   ExInitializeFastMutex(&HandleTable->ListLock);
504 }
505
506 VOID FASTCALL
507 ObmFreeHandleTable(PUSER_HANDLE_TABLE HandleTable)
508 {
509   ObmpDeleteHandleTable(HandleTable);
510 }
511
512 PUSER_HANDLE_TABLE FASTCALL
513 ObmCreateHandleTable(VOID)
514 {
515   PUSER_HANDLE_TABLE HandleTable;
516
517   HandleTable = (PUSER_HANDLE_TABLE)ExAllocatePool(NonPagedPool, 
518                                                    sizeof(USER_HANDLE_TABLE));
519   if (!HandleTable)
520     {
521       return NULL;
522     }
523   
524   ObmInitializeHandleTable(HandleTable);
525   
526   return HandleTable;
527 }
528
529 VOID FASTCALL
530 ObmDestroyHandleTable(PUSER_HANDLE_TABLE HandleTable)
531 {
532   ObmFreeHandleTable(HandleTable);
533   ExFreePool(HandleTable);
534 }
535
536 /* EOF */