c8b7c1883cec00358f32ea4bfbf312f45cfa432c
[reactos.git] / ntoskrnl / ob / object.c
1 /* $Id$
2  * 
3  * COPYRIGHT:     See COPYING in the top level directory
4  * PROJECT:       ReactOS kernel
5  * FILE:          ntoskrnl/ob/object.c
6  * PURPOSE:       Implements generic object managment functions
7  * PROGRAMMER:    David Welch (welch@cwcom.net)
8  * UPDATE HISTORY:
9  *               10/06/98: Created
10  */
11
12 /* INCLUDES *****************************************************************/
13
14 #define NTOS_MODE_KERNEL
15 #include <ntos.h>
16 #include <roscfg.h>
17 #include <internal/ob.h>
18 #include <internal/ps.h>
19 #include <internal/id.h>
20 #include <internal/ke.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25
26 /* FUNCTIONS ************************************************************/
27
28 PVOID HEADER_TO_BODY(POBJECT_HEADER obj)
29 {
30    return(((void *)obj)+sizeof(OBJECT_HEADER)-sizeof(COMMON_BODY_HEADER));
31 }
32
33
34 POBJECT_HEADER BODY_TO_HEADER(PVOID body)
35 {
36    PCOMMON_BODY_HEADER chdr = (PCOMMON_BODY_HEADER)body;
37    return(CONTAINING_RECORD((&(chdr->Type)),OBJECT_HEADER,Type));
38 }
39
40
41 /**********************************************************************
42  * NAME                                                 PRIVATE
43  *      ObInitializeObject
44  *
45  * DESCRIPTION
46  *
47  * ARGUMENTS
48  *
49  * RETURN VALUE
50  */
51 VOID ObInitializeObject(POBJECT_HEADER ObjectHeader,
52                         PHANDLE Handle,
53                         ACCESS_MASK DesiredAccess,
54                         POBJECT_TYPE Type,
55                         POBJECT_ATTRIBUTES ObjectAttributes)
56 {
57    ObjectHeader->HandleCount = 0;
58    ObjectHeader->RefCount = 1;
59    ObjectHeader->ObjectType = Type;
60    if (ObjectAttributes != NULL &&
61        ObjectAttributes->Attributes & OBJ_PERMANENT)
62      {
63         ObjectHeader->Permanent = TRUE;
64      }
65    else
66      {
67         ObjectHeader->Permanent = FALSE;
68      }
69    RtlInitUnicodeString(&(ObjectHeader->Name),NULL);
70    if (Handle != NULL)
71      {
72         ObCreateHandle(PsGetCurrentProcess(),
73                        HEADER_TO_BODY(ObjectHeader),
74                        DesiredAccess,
75                        ObjectAttributes && (ObjectAttributes->Attributes & OBJ_INHERIT) ? TRUE : FALSE,
76                        Handle);
77      }
78 }
79
80
81 /**********************************************************************
82  * NAME                                                 PRIVATE
83  *      ObFindObject@16
84  *
85  * DESCRIPTION
86  *
87  * ARGUMENTS
88  *      ObjectAttributes
89  *
90  *      ReturnedObject
91  *
92  *      RemainigPath
93  *              Pointer to a unicode string that will contain the
94  *              remaining path if the function returns successfully.
95  *              The caller must free the buffer after use by calling
96  *              RtlFreeUnicodeString ().
97  *
98  *      ObjectType
99  *              Optional pointer to an object type. This is used to
100  *              descide if a symbolic link object will be parsed or not.
101  *
102  * RETURN VALUE
103  */
104 NTSTATUS
105 ObFindObject(POBJECT_ATTRIBUTES ObjectAttributes,
106              PVOID* ReturnedObject,
107              PUNICODE_STRING RemainingPath,
108              POBJECT_TYPE ObjectType)
109 {
110    PVOID NextObject;
111    PVOID CurrentObject;
112    PVOID RootObject;
113    POBJECT_HEADER CurrentHeader;
114    NTSTATUS Status;
115    PWSTR current;
116    UNICODE_STRING PathString;
117    ULONG Attributes;
118    PUNICODE_STRING ObjectName;
119
120    DPRINT("ObFindObject(ObjectAttributes %x, ReturnedObject %x, "
121           "RemainingPath %x)\n",ObjectAttributes,ReturnedObject,RemainingPath);
122    DPRINT("ObjectAttributes->ObjectName %wZ\n",
123           ObjectAttributes->ObjectName);
124
125    RtlInitUnicodeString (RemainingPath, NULL);
126
127    if (ObjectAttributes->RootDirectory == NULL)
128      {
129         ObReferenceObjectByPointer(NameSpaceRoot,
130                                    DIRECTORY_TRAVERSE,
131                                    NULL,
132                                    UserMode);
133         CurrentObject = NameSpaceRoot;
134      }
135    else
136      {
137         Status = ObReferenceObjectByHandle(ObjectAttributes->RootDirectory,
138                                            DIRECTORY_TRAVERSE,
139                                            NULL,
140                                            UserMode,
141                                            &CurrentObject,
142                                            NULL);
143         if (!NT_SUCCESS(Status))
144           {
145              return(Status);
146           }
147      }
148
149   ObjectName = ObjectAttributes->ObjectName;
150   if (ObjectName->Length == 0 ||
151       ObjectName->Buffer[0] == UNICODE_NULL)
152     {
153       *ReturnedObject = CurrentObject;
154       return STATUS_SUCCESS;
155     }
156
157   if (ObjectAttributes->RootDirectory == NULL &&
158       ObjectName->Buffer[0] != L'\\')
159     {
160       ObDereferenceObject (CurrentObject);
161       return STATUS_UNSUCCESSFUL;
162     }
163
164   /* Create a zero-terminated copy of the object name */
165   PathString.Length = ObjectName->Length;
166   PathString.MaximumLength = ObjectName->Length + sizeof(WCHAR);
167   PathString.Buffer = ExAllocatePool (NonPagedPool,
168                                       PathString.MaximumLength);
169   if (PathString.Buffer == NULL)
170     {
171       ObDereferenceObject (CurrentObject);
172       return STATUS_INSUFFICIENT_RESOURCES;
173     }
174
175   RtlCopyMemory (PathString.Buffer,
176                  ObjectName->Buffer,
177                  ObjectName->Length);
178   PathString.Buffer[PathString.Length / sizeof(WCHAR)] = UNICODE_NULL;
179
180   current = PathString.Buffer;
181
182    RootObject = CurrentObject;
183    Attributes = ObjectAttributes->Attributes;
184    if (ObjectType == ObSymbolicLinkType)
185      Attributes |= OBJ_OPENLINK;
186
187    while (TRUE)
188      {
189         DPRINT("current %S\n",current);
190         CurrentHeader = BODY_TO_HEADER(CurrentObject);
191
192         DPRINT("Current ObjectType %wZ\n",
193                &CurrentHeader->ObjectType->TypeName);
194
195         if (CurrentHeader->ObjectType->Parse == NULL)
196           {
197              DPRINT("Current object can't parse\n");
198              break;
199           }
200         Status = CurrentHeader->ObjectType->Parse(CurrentObject,
201                                                   &NextObject,
202                                                   &PathString,
203                                                   &current,
204                                                   Attributes);
205         if (Status == STATUS_REPARSE)
206           {
207              /* reparse the object path */
208              NextObject = RootObject;
209              current = PathString.Buffer;
210              
211              ObReferenceObjectByPointer(NextObject,
212                                         DIRECTORY_TRAVERSE,
213                                         NULL,
214                                         UserMode);
215           }
216
217         if (NextObject == NULL)
218           {
219              break;
220           }
221         ObDereferenceObject(CurrentObject);
222         CurrentObject = NextObject;
223      }
224    
225    if (current)
226       RtlCreateUnicodeString (RemainingPath, current);
227    RtlFreeUnicodeString (&PathString);
228    *ReturnedObject = CurrentObject;
229    
230    return(STATUS_SUCCESS);
231 }
232
233
234 /**********************************************************************
235  * NAME                                                 EXPORTED
236  *      ObQueryNameString@16
237  *
238  * DESCRIPTION
239  *
240  * ARGUMENTS
241  *
242  * RETURN VALUE
243  *
244  * @implemented
245  */
246 NTSTATUS STDCALL
247 ObQueryNameString (IN PVOID Object,
248                    OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
249                    IN ULONG Length,
250                    OUT PULONG ReturnLength)
251 {
252   POBJECT_NAME_INFORMATION LocalInfo;
253   POBJECT_HEADER ObjectHeader;
254   ULONG LocalReturnLength;
255   NTSTATUS Status;
256
257   *ReturnLength = 0;
258
259   if (Length < sizeof(OBJECT_NAME_INFORMATION) + sizeof(WCHAR))
260     return STATUS_INVALID_BUFFER_SIZE;
261
262   ObjectNameInfo->Name.MaximumLength = Length - sizeof(OBJECT_NAME_INFORMATION);
263   ObjectNameInfo->Name.Length = 0;
264   ObjectNameInfo->Name.Buffer =
265     (PWCHAR)((ULONG_PTR)ObjectNameInfo + sizeof(OBJECT_NAME_INFORMATION));
266   ObjectNameInfo->Name.Buffer[0] = 0;
267
268   ObjectHeader = BODY_TO_HEADER(Object);
269
270   if (ObjectHeader->ObjectType != NULL &&
271       ObjectHeader->ObjectType->QueryName != NULL)
272     {
273       DPRINT ("Calling %x\n", ObjectHeader->ObjectType->QueryName);
274       Status = ObjectHeader->ObjectType->QueryName (Object,
275                                                     ObjectNameInfo,
276                                                     Length,
277                                                     ReturnLength);
278     }
279   else if (ObjectHeader->Name.Length > 0 && ObjectHeader->Name.Buffer != NULL)
280     {
281       DPRINT ("Object does not have a 'QueryName' function\n");
282
283       if (ObjectHeader->Parent == NameSpaceRoot)
284         {
285           DPRINT ("Reached the root directory\n");
286           ObjectNameInfo->Name.Length = 0;
287           ObjectNameInfo->Name.Buffer[0] = 0;
288           Status = STATUS_SUCCESS;
289         }
290       else if (ObjectHeader->Parent != NULL)
291         {
292           LocalInfo = ExAllocatePool (NonPagedPool,
293                                       sizeof(OBJECT_NAME_INFORMATION) +
294                                       MAX_PATH * sizeof(WCHAR));
295           if (LocalInfo == NULL)
296             return STATUS_INSUFFICIENT_RESOURCES;
297
298           Status = ObQueryNameString (ObjectHeader->Parent,
299                                       LocalInfo,
300                                       MAX_PATH * sizeof(WCHAR),
301                                       &LocalReturnLength);
302           if (!NT_SUCCESS (Status))
303             {
304               ExFreePool (LocalInfo);
305               return Status;
306             }
307
308           Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
309                                                    &LocalInfo->Name);
310
311           ExFreePool (LocalInfo);
312
313           if (!NT_SUCCESS (Status))
314             return Status;
315         }
316
317       DPRINT ("Object path %wZ\n", &ObjectHeader->Name);
318       Status = RtlAppendUnicodeToString (&ObjectNameInfo->Name,
319                                          L"\\");
320       if (!NT_SUCCESS (Status))
321         return Status;
322
323       Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
324                                                &ObjectHeader->Name);
325     }
326   else
327     {
328       DPRINT ("Object is unnamed\n");
329
330       ObjectNameInfo->Name.MaximumLength = 0;
331       ObjectNameInfo->Name.Length = 0;
332       ObjectNameInfo->Name.Buffer = NULL;
333
334       Status = STATUS_SUCCESS;
335     }
336
337   if (NT_SUCCESS (Status))
338     {
339       ObjectNameInfo->Name.MaximumLength =
340         (ObjectNameInfo->Name.Length) ? ObjectNameInfo->Name.Length + sizeof(WCHAR) : 0;
341       *ReturnLength =
342         sizeof(OBJECT_NAME_INFORMATION) + ObjectNameInfo->Name.MaximumLength;
343       DPRINT ("Returned object path: %wZ\n", &ObjectNameInfo->Name);
344     }
345
346   return Status;
347 }
348
349
350 /**********************************************************************
351  * NAME                                                 EXPORTED
352  *      ObRosCreateObject@20
353  *
354  * DESCRIPTION
355  *
356  * ARGUMENTS
357  *
358  * NOTE
359  *   Internal ReactOS function
360  * RETURN VALUE
361  */
362 NTSTATUS STDCALL
363 ObRosCreateObject (OUT PHANDLE Handle,
364                 IN ACCESS_MASK DesiredAccess,
365                 IN POBJECT_ATTRIBUTES ObjectAttributes,
366                 IN POBJECT_TYPE Type,
367                 OUT PVOID *Object)
368 {
369   PVOID Parent = NULL;
370   UNICODE_STRING RemainingPath;
371   POBJECT_HEADER Header;
372   POBJECT_HEADER ParentHeader = NULL;
373   NTSTATUS Status;
374   BOOLEAN ObjectAttached = FALSE;
375   PWCHAR NamePtr;
376
377   assert_irql(APC_LEVEL);
378
379   DPRINT("ObRosCreateObject(Handle %x, ObjectAttributes %x, Type %x)\n",
380          Handle, ObjectAttributes, Type);
381
382   if (ObjectAttributes != NULL &&
383       ObjectAttributes->ObjectName != NULL &&
384       ObjectAttributes->ObjectName->Buffer != NULL)
385     {
386       Status = ObFindObject(ObjectAttributes,
387                             &Parent,
388                             &RemainingPath,
389                             NULL);
390       if (!NT_SUCCESS(Status))
391         {
392           DPRINT("ObFindObject() failed! (Status 0x%x)\n", Status);
393           return(Status);
394         }
395     }
396   else
397     {
398       RtlInitUnicodeString(&RemainingPath, NULL);
399     }
400
401   RtlMapGenericMask(&DesiredAccess,
402                     Type->Mapping);
403
404   Header = (POBJECT_HEADER)ExAllocatePoolWithTag(NonPagedPool,
405                                                  OBJECT_ALLOC_SIZE(Type),
406                                                  Type->Tag);
407   ObInitializeObject(Header,
408                      NULL,
409                      DesiredAccess,
410                      Type,
411                      ObjectAttributes);
412
413   if (Parent != NULL)
414     {
415       ParentHeader = BODY_TO_HEADER(Parent);
416     }
417
418   if (ParentHeader != NULL &&
419       ParentHeader->ObjectType == ObDirectoryType &&
420       RemainingPath.Buffer != NULL)
421     {
422       NamePtr = RemainingPath.Buffer;
423       if (*NamePtr == L'\\')
424         NamePtr++;
425
426       ObpAddEntryDirectory(Parent,
427                            Header,
428                            NamePtr);
429
430       ObjectAttached = TRUE;
431     }
432
433   if ((Header->ObjectType != NULL) &&
434       (Header->ObjectType->Create != NULL))
435     {
436       DPRINT("Calling %x\n", Header->ObjectType->Create);
437       Status = Header->ObjectType->Create(HEADER_TO_BODY(Header),
438                                           Parent,
439                                           RemainingPath.Buffer,
440                                           ObjectAttributes);
441       if (!NT_SUCCESS(Status))
442         {
443           if (ObjectAttached == TRUE)
444             {
445               ObpRemoveEntryDirectory(Header);
446             }
447           if (Parent)
448             {
449               ObDereferenceObject(Parent);
450             }
451           RtlFreeUnicodeString(&Header->Name);
452           RtlFreeUnicodeString(&RemainingPath);
453           ExFreePool(Header);
454           return(Status);
455         }
456     }
457   RtlFreeUnicodeString( &RemainingPath );
458
459   if (Object != NULL)
460     {
461       *Object = HEADER_TO_BODY(Header);
462     }
463
464   if (Handle != NULL)
465   {
466      ObCreateHandle(PsGetCurrentProcess(),
467                     *Object,
468                     DesiredAccess,
469                     ObjectAttributes && (ObjectAttributes->Attributes & OBJ_INHERIT) ? TRUE : FALSE,
470                     Handle);
471   }
472
473   return(STATUS_SUCCESS);
474 }
475
476 /**********************************************************************
477  * NAME                                                 EXPORTED
478  *      ObCreateObject@36
479  *
480  * DESCRIPTION
481  *
482  * ARGUMENTS
483  *
484  * RETURN VALUE
485  *
486  * @unimplemented
487  */
488 NTSTATUS STDCALL
489 ObCreateObject (IN KPROCESSOR_MODE      ObjectAttributesAccessMode OPTIONAL,
490   IN POBJECT_TYPE         ObjectType,
491   IN POBJECT_ATTRIBUTES   ObjectAttributes OPTIONAL,
492   IN KPROCESSOR_MODE      AccessMode,
493   IN OUT PVOID            ParseContext OPTIONAL,
494   IN ULONG                ObjectSize,
495   IN ULONG                PagedPoolCharge OPTIONAL,
496   IN ULONG                NonPagedPoolCharge OPTIONAL,
497   OUT PVOID               *Object)
498 {
499   UNIMPLEMENTED
500   return STATUS_NOT_IMPLEMENTED;
501 }
502
503 /*
504  * @implemented
505  */
506 NTSTATUS STDCALL
507 ObReferenceObjectByPointer(IN PVOID Object,
508                            IN ACCESS_MASK DesiredAccess,
509                            IN POBJECT_TYPE ObjectType,
510                            IN KPROCESSOR_MODE AccessMode)
511 /*
512  * FUNCTION: Increments the pointer reference count for a given object
513  * ARGUMENTS:
514  *         ObjectBody = Object's body
515  *         DesiredAccess = Desired access to the object
516  *         ObjectType = Points to the object type structure
517  *         AccessMode = Type of access check to perform
518  * RETURNS: Status
519  */
520 {
521    POBJECT_HEADER Header;
522
523 //   DPRINT("ObReferenceObjectByPointer(Object %x, ObjectType %x)\n",
524 //        Object,ObjectType);
525    
526    Header = BODY_TO_HEADER(Object);
527    
528    if (ObjectType != NULL && Header->ObjectType != ObjectType)
529      {
530         DPRINT("Failed %x (type was %x %S) should be %x %S\n",
531                 Header,
532                 Header->ObjectType,
533                 Header->ObjectType->TypeName.Buffer,
534                 ObjectType,
535     ObjectType->TypeName.Buffer);
536         return(STATUS_UNSUCCESSFUL);
537      }
538    if (Header->ObjectType == PsProcessType)
539      {
540         DPRINT("Ref p 0x%x refcount %d type %x ",
541                 Object, Header->RefCount, PsProcessType);
542         DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
543      }
544    if (Header->ObjectType == PsThreadType)
545      {
546         DPRINT("Deref t 0x%x with refcount %d type %x ",
547                 Object, Header->RefCount, PsThreadType);
548         DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
549      }
550  
551    if (Header->CloseInProcess)
552    {
553       return(STATUS_UNSUCCESSFUL);
554    }
555
556    InterlockedIncrement(&Header->RefCount);
557    
558    return(STATUS_SUCCESS);
559 }
560
561
562 /*
563  * @implemented
564  */
565 NTSTATUS STDCALL
566 ObOpenObjectByPointer(IN POBJECT Object,
567                       IN ULONG HandleAttributes,
568                       IN PACCESS_STATE PassedAccessState,
569                       IN ACCESS_MASK DesiredAccess,
570                       IN POBJECT_TYPE ObjectType,
571                       IN KPROCESSOR_MODE AccessMode,
572                       OUT PHANDLE Handle)
573 {
574    NTSTATUS Status;
575    
576    DPRINT("ObOpenObjectByPointer()\n");
577    
578    Status = ObReferenceObjectByPointer(Object,
579                                        0,
580                                        ObjectType,
581                                        AccessMode);
582    if (!NT_SUCCESS(Status))
583      {
584         return Status;
585      }
586    
587    Status = ObCreateHandle(PsGetCurrentProcess(),
588                            Object,
589                            DesiredAccess,
590                            HandleAttributes & OBJ_INHERIT,
591                            Handle);
592    
593    ObDereferenceObject(Object);
594    
595    return STATUS_SUCCESS;
596 }
597
598
599 static NTSTATUS
600 ObpPerformRetentionChecks(POBJECT_HEADER Header)
601 {
602 //  DPRINT("ObPerformRetentionChecks(Header %x), RefCount %d, HandleCount %d\n",
603 //        Header,Header->RefCount,Header->HandleCount);
604   
605   if (Header->RefCount < 0)
606     {
607       CPRINT("Object %x/%x has invalid reference count (%d)\n",
608              Header, HEADER_TO_BODY(Header), Header->RefCount);
609       KEBUGCHECK(0);
610     }
611   if (Header->HandleCount < 0)
612     {
613       CPRINT("Object %x/%x has invalid handle count (%d)\n",
614              Header, HEADER_TO_BODY(Header), Header->HandleCount);
615       KEBUGCHECK(0);
616     }
617   
618   if (Header->RefCount == 0 &&
619       Header->HandleCount == 0 &&
620       Header->Permanent == FALSE)
621     {
622       if (Header->CloseInProcess)
623       {
624          KEBUGCHECK(0);
625          return STATUS_UNSUCCESSFUL;
626       }
627       Header->CloseInProcess = TRUE;
628       if (Header->ObjectType != NULL &&
629           Header->ObjectType->Delete != NULL)
630         {
631           Header->ObjectType->Delete(HEADER_TO_BODY(Header));
632         }
633       if (Header->Name.Buffer != NULL)
634         {
635           ObpRemoveEntryDirectory(Header);
636           RtlFreeUnicodeString(&Header->Name);
637         }
638       DPRINT("ObPerformRetentionChecks() = Freeing object\n");
639       ExFreePool(Header);
640     }
641   return(STATUS_SUCCESS);
642 }
643
644
645 /**********************************************************************
646  * NAME                                                 EXPORTED
647  *      ObfReferenceObject@4
648  *
649  * DESCRIPTION
650  *      Increments a given object's reference count and performs
651  *      retention checks.
652  *
653  * ARGUMENTS
654  *  ObjectBody = Body of the object.
655  *
656  * RETURN VALUE
657  *      None.
658  *
659  * @implemented
660  */
661 VOID FASTCALL
662 ObfReferenceObject(IN PVOID Object)
663 {
664   POBJECT_HEADER Header;
665
666   assert(Object);
667
668   Header = BODY_TO_HEADER(Object);
669
670   if (Header->CloseInProcess)
671   {
672       KEBUGCHECK(0);
673   }
674   InterlockedIncrement(&Header->RefCount);
675
676   ObpPerformRetentionChecks(Header);
677 }
678
679
680 /**********************************************************************
681  * NAME                                                 EXPORTED
682  *      ObfDereferenceObject@4
683  *
684  * DESCRIPTION
685  *      Decrements a given object's reference count and performs
686  *      retention checks.
687  *
688  * ARGUMENTS
689  *      ObjectBody = Body of the object.
690  *
691  * RETURN VALUE
692  *      None.
693  *
694  * @implemented
695  */
696 VOID FASTCALL
697 ObfDereferenceObject(IN PVOID Object)
698 {
699   POBJECT_HEADER Header;
700   extern POBJECT_TYPE PsProcessType;
701
702   assert(Object);
703
704   Header = BODY_TO_HEADER(Object);
705
706   if (Header->ObjectType == PsProcessType)
707     {
708       DPRINT("Deref p 0x%x with refcount %d type %x ",
709              Object, Header->RefCount, PsProcessType);
710       DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
711     }
712   if (Header->ObjectType == PsThreadType)
713     {
714       DPRINT("Deref t 0x%x with refcount %d type %x ",
715              Object, Header->RefCount, PsThreadType);
716       DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
717     }
718   
719   InterlockedDecrement(&Header->RefCount);
720   
721   ObpPerformRetentionChecks(Header);
722 }
723
724
725 /**********************************************************************
726  * NAME                                                 EXPORTED
727  *      ObGetObjectPointerCount@4
728  *
729  * DESCRIPTION
730  *      Retrieves the pointer(reference) count of the given object.
731  *
732  * ARGUMENTS
733  *      ObjectBody = Body of the object.
734  *
735  * RETURN VALUE
736  *      Reference count.
737  *
738  * @implemented
739  */
740 ULONG STDCALL
741 ObGetObjectPointerCount(PVOID Object)
742 {
743   POBJECT_HEADER Header;
744
745   assert(Object);
746   Header = BODY_TO_HEADER(Object);
747
748   return(Header->RefCount);
749 }
750
751
752 /**********************************************************************
753  * NAME                                                 INTERNAL
754  *      ObGetObjectHandleCount@4
755  *
756  * DESCRIPTION
757  *      Retrieves the handle count of the given object.
758  *
759  * ARGUMENTS
760  *      ObjectBody = Body of the object.
761  *
762  * RETURN VALUE
763  *      Reference count.
764  */
765 ULONG
766 ObGetObjectHandleCount(PVOID Object)
767 {
768   POBJECT_HEADER Header;
769
770   assert(Object);
771   Header = BODY_TO_HEADER(Object);
772
773   return(Header->HandleCount);
774 }
775
776 /* EOF */