1 /* $Id: object.c,v 1.81 2004/07/22 18:38:08 ekohl Exp $
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 * PROGRAMMERS David Welch (welch@cwcom.net), Skywing (skywing@valhallalegends.com)
10 * 09/13/03: Fixed various ObXxx routines to not call retention
11 * checks directly at a raised IRQL.
14 /* INCLUDES *****************************************************************/
16 #define NTOS_MODE_KERNEL
19 #include <internal/ob.h>
20 #include <internal/ps.h>
21 #include <internal/id.h>
22 #include <internal/ke.h>
25 #include <internal/debug.h>
28 typedef struct _RETENTION_CHECK_PARAMS
30 WORK_QUEUE_ITEM WorkItem
;
31 POBJECT_HEADER ObjectHeader
;
32 } RETENTION_CHECK_PARAMS
, *PRETENTION_CHECK_PARAMS
;
35 /* FUNCTIONS ************************************************************/
37 PVOID
HEADER_TO_BODY(POBJECT_HEADER obj
)
39 return(((char*)obj
)+sizeof(OBJECT_HEADER
)-sizeof(COMMON_BODY_HEADER
));
43 POBJECT_HEADER
BODY_TO_HEADER(PVOID body
)
45 PCOMMON_BODY_HEADER chdr
= (PCOMMON_BODY_HEADER
)body
;
46 return(CONTAINING_RECORD((&(chdr
->Type
)),OBJECT_HEADER
,Type
));
50 /**********************************************************************
62 * Pointer to a unicode string that will contain the
63 * remaining path if the function returns successfully.
64 * The caller must free the buffer after use by calling
65 * RtlFreeUnicodeString ().
68 * Optional pointer to an object type. This is used to
69 * descide if a symbolic link object will be parsed or not.
74 ObFindObject(POBJECT_ATTRIBUTES ObjectAttributes
,
75 PVOID
* ReturnedObject
,
76 PUNICODE_STRING RemainingPath
,
77 POBJECT_TYPE ObjectType
)
82 POBJECT_HEADER CurrentHeader
;
85 UNICODE_STRING PathString
;
87 PUNICODE_STRING ObjectName
;
89 DPRINT("ObFindObject(ObjectAttributes %x, ReturnedObject %x, "
90 "RemainingPath %x)\n",ObjectAttributes
,ReturnedObject
,RemainingPath
);
91 DPRINT("ObjectAttributes->ObjectName %wZ\n",
92 ObjectAttributes
->ObjectName
);
94 RtlInitUnicodeString (RemainingPath
, NULL
);
96 if (ObjectAttributes
->RootDirectory
== NULL
)
98 ObReferenceObjectByPointer(NameSpaceRoot
,
102 CurrentObject
= NameSpaceRoot
;
106 Status
= ObReferenceObjectByHandle(ObjectAttributes
->RootDirectory
,
112 if (!NT_SUCCESS(Status
))
118 ObjectName
= ObjectAttributes
->ObjectName
;
119 if (ObjectName
->Length
== 0 ||
120 ObjectName
->Buffer
[0] == UNICODE_NULL
)
122 *ReturnedObject
= CurrentObject
;
123 return STATUS_SUCCESS
;
126 if (ObjectAttributes
->RootDirectory
== NULL
&&
127 ObjectName
->Buffer
[0] != L
'\\')
129 ObDereferenceObject (CurrentObject
);
130 return STATUS_UNSUCCESSFUL
;
133 /* Create a zero-terminated copy of the object name */
134 PathString
.Length
= ObjectName
->Length
;
135 PathString
.MaximumLength
= ObjectName
->Length
+ sizeof(WCHAR
);
136 PathString
.Buffer
= ExAllocatePool (NonPagedPool
,
137 PathString
.MaximumLength
);
138 if (PathString
.Buffer
== NULL
)
140 ObDereferenceObject (CurrentObject
);
141 return STATUS_INSUFFICIENT_RESOURCES
;
144 RtlCopyMemory (PathString
.Buffer
,
147 PathString
.Buffer
[PathString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
149 current
= PathString
.Buffer
;
151 RootObject
= CurrentObject
;
152 Attributes
= ObjectAttributes
->Attributes
;
153 if (ObjectType
== ObSymbolicLinkType
)
154 Attributes
|= OBJ_OPENLINK
;
158 DPRINT("current %S\n",current
);
159 CurrentHeader
= BODY_TO_HEADER(CurrentObject
);
161 DPRINT("Current ObjectType %wZ\n",
162 &CurrentHeader
->ObjectType
->TypeName
);
164 if (CurrentHeader
->ObjectType
->Parse
== NULL
)
166 DPRINT("Current object can't parse\n");
169 Status
= CurrentHeader
->ObjectType
->Parse(CurrentObject
,
174 if (Status
== STATUS_REPARSE
)
176 /* reparse the object path */
177 NextObject
= NameSpaceRoot
;
178 current
= PathString
.Buffer
;
180 ObReferenceObjectByPointer(NextObject
,
186 if (NextObject
== NULL
)
190 ObDereferenceObject(CurrentObject
);
191 CurrentObject
= NextObject
;
195 RtlCreateUnicodeString (RemainingPath
, current
);
196 RtlFreeUnicodeString (&PathString
);
197 *ReturnedObject
= CurrentObject
;
199 return STATUS_SUCCESS
;
203 /**********************************************************************
205 * ObQueryNameString@16
216 ObQueryNameString (IN PVOID Object
,
217 OUT POBJECT_NAME_INFORMATION ObjectNameInfo
,
219 OUT PULONG ReturnLength
)
221 POBJECT_NAME_INFORMATION LocalInfo
;
222 POBJECT_HEADER ObjectHeader
;
223 ULONG LocalReturnLength
;
228 if (Length
< sizeof(OBJECT_NAME_INFORMATION
) + sizeof(WCHAR
))
229 return STATUS_INVALID_BUFFER_SIZE
;
231 ObjectNameInfo
->Name
.MaximumLength
= (USHORT
)(Length
- sizeof(OBJECT_NAME_INFORMATION
));
232 ObjectNameInfo
->Name
.Length
= 0;
233 ObjectNameInfo
->Name
.Buffer
=
234 (PWCHAR
)((ULONG_PTR
)ObjectNameInfo
+ sizeof(OBJECT_NAME_INFORMATION
));
235 ObjectNameInfo
->Name
.Buffer
[0] = 0;
237 ObjectHeader
= BODY_TO_HEADER(Object
);
239 if (ObjectHeader
->ObjectType
!= NULL
&&
240 ObjectHeader
->ObjectType
->QueryName
!= NULL
)
242 DPRINT ("Calling %x\n", ObjectHeader
->ObjectType
->QueryName
);
243 Status
= ObjectHeader
->ObjectType
->QueryName (Object
,
248 else if (ObjectHeader
->Name
.Length
> 0 && ObjectHeader
->Name
.Buffer
!= NULL
)
250 DPRINT ("Object does not have a 'QueryName' function\n");
252 if (ObjectHeader
->Parent
== NameSpaceRoot
)
254 DPRINT ("Reached the root directory\n");
255 ObjectNameInfo
->Name
.Length
= 0;
256 ObjectNameInfo
->Name
.Buffer
[0] = 0;
257 Status
= STATUS_SUCCESS
;
259 else if (ObjectHeader
->Parent
!= NULL
)
261 LocalInfo
= ExAllocatePool (NonPagedPool
,
262 sizeof(OBJECT_NAME_INFORMATION
) +
263 MAX_PATH
* sizeof(WCHAR
));
264 if (LocalInfo
== NULL
)
265 return STATUS_INSUFFICIENT_RESOURCES
;
267 Status
= ObQueryNameString (ObjectHeader
->Parent
,
269 MAX_PATH
* sizeof(WCHAR
),
271 if (!NT_SUCCESS (Status
))
273 ExFreePool (LocalInfo
);
277 Status
= RtlAppendUnicodeStringToString (&ObjectNameInfo
->Name
,
280 ExFreePool (LocalInfo
);
282 if (!NT_SUCCESS (Status
))
286 DPRINT ("Object path %wZ\n", &ObjectHeader
->Name
);
287 Status
= RtlAppendUnicodeToString (&ObjectNameInfo
->Name
,
289 if (!NT_SUCCESS (Status
))
292 Status
= RtlAppendUnicodeStringToString (&ObjectNameInfo
->Name
,
293 &ObjectHeader
->Name
);
297 DPRINT ("Object is unnamed\n");
299 ObjectNameInfo
->Name
.MaximumLength
= 0;
300 ObjectNameInfo
->Name
.Length
= 0;
301 ObjectNameInfo
->Name
.Buffer
= NULL
;
303 Status
= STATUS_SUCCESS
;
306 if (NT_SUCCESS (Status
))
308 ObjectNameInfo
->Name
.MaximumLength
=
309 (ObjectNameInfo
->Name
.Length
) ? ObjectNameInfo
->Name
.Length
+ sizeof(WCHAR
) : 0;
311 sizeof(OBJECT_NAME_INFORMATION
) + ObjectNameInfo
->Name
.MaximumLength
;
312 DPRINT ("Returned object path: %wZ\n", &ObjectNameInfo
->Name
);
319 /**********************************************************************
333 ObCreateObject (IN KPROCESSOR_MODE ObjectAttributesAccessMode OPTIONAL
,
334 IN POBJECT_TYPE Type
,
335 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
336 IN KPROCESSOR_MODE AccessMode
,
337 IN OUT PVOID ParseContext OPTIONAL
,
339 IN ULONG PagedPoolCharge OPTIONAL
,
340 IN ULONG NonPagedPoolCharge OPTIONAL
,
344 UNICODE_STRING RemainingPath
;
345 POBJECT_HEADER Header
;
346 POBJECT_HEADER ParentHeader
= NULL
;
348 BOOLEAN ObjectAttached
= FALSE
;
350 PSECURITY_DESCRIPTOR NewSecurityDescriptor
= NULL
;
351 SECURITY_SUBJECT_CONTEXT SubjectContext
;
353 assert_irql(APC_LEVEL
);
355 DPRINT("ObCreateObject(Type %p ObjectAttributes %p, Object %p)\n",
356 Type
, ObjectAttributes
, Object
);
360 DPRINT1("Invalid object type!\n");
361 return STATUS_INVALID_PARAMETER
;
364 if (ObjectAttributes
!= NULL
&&
365 ObjectAttributes
->ObjectName
!= NULL
&&
366 ObjectAttributes
->ObjectName
->Buffer
!= NULL
)
368 Status
= ObFindObject(ObjectAttributes
,
372 if (!NT_SUCCESS(Status
))
374 DPRINT("ObFindObject() failed! (Status 0x%x)\n", Status
);
380 RtlInitUnicodeString(&RemainingPath
, NULL
);
383 Header
= (POBJECT_HEADER
)ExAllocatePoolWithTag(NonPagedPool
,
384 OBJECT_ALLOC_SIZE(ObjectSize
),
387 return STATUS_INSUFFICIENT_RESOURCES
;
388 RtlZeroMemory(Header
, OBJECT_ALLOC_SIZE(ObjectSize
));
390 /* Initialize the object header */
391 Header
->HandleCount
= 0;
392 Header
->RefCount
= 1;
393 Header
->ObjectType
= Type
;
394 if (ObjectAttributes
!= NULL
&&
395 ObjectAttributes
->Attributes
& OBJ_PERMANENT
)
397 Header
->Permanent
= TRUE
;
401 Header
->Permanent
= FALSE
;
404 if (ObjectAttributes
!= NULL
&&
405 ObjectAttributes
->Attributes
& OBJ_INHERIT
)
407 Header
->Inherit
= TRUE
;
411 Header
->Inherit
= FALSE
;
414 RtlInitUnicodeString(&(Header
->Name
),NULL
);
418 ParentHeader
= BODY_TO_HEADER(Parent
);
421 if (ParentHeader
!= NULL
&&
422 ParentHeader
->ObjectType
== ObDirectoryType
&&
423 RemainingPath
.Buffer
!= NULL
)
425 NamePtr
= RemainingPath
.Buffer
;
426 if (*NamePtr
== L
'\\')
429 ObpAddEntryDirectory(Parent
,
433 ObjectAttached
= TRUE
;
436 if (Header
->ObjectType
->Create
!= NULL
)
438 DPRINT("Calling %x\n", Header
->ObjectType
->Create
);
439 Status
= Header
->ObjectType
->Create(HEADER_TO_BODY(Header
),
441 RemainingPath
.Buffer
,
443 if (!NT_SUCCESS(Status
))
445 if (ObjectAttached
== TRUE
)
447 ObpRemoveEntryDirectory(Header
);
451 ObDereferenceObject(Parent
);
453 RtlFreeUnicodeString(&Header
->Name
);
454 RtlFreeUnicodeString(&RemainingPath
);
459 RtlFreeUnicodeString(&RemainingPath
);
461 SeCaptureSubjectContext(&SubjectContext
);
463 /* Build the new security descriptor */
464 Status
= SeAssignSecurity((ParentHeader
!= NULL
) ? ParentHeader
->SecurityDescriptor
: NULL
,
465 (ObjectAttributes
!= NULL
) ? ObjectAttributes
->SecurityDescriptor
: NULL
,
466 &NewSecurityDescriptor
,
467 (Header
->ObjectType
== ObDirectoryType
),
469 Header
->ObjectType
->Mapping
,
471 if (NT_SUCCESS(Status
))
473 DPRINT("NewSecurityDescriptor %p\n", NewSecurityDescriptor
);
475 if (Header
->ObjectType
->Security
!= NULL
)
477 /* Call the security method */
478 Status
= Header
->ObjectType
->Security(HEADER_TO_BODY(Header
),
479 AssignSecurityDescriptor
,
481 NewSecurityDescriptor
,
484 Status
= STATUS_SUCCESS
;
489 /* Assign the security descriptor to the object header */
490 Status
= ObpAddSecurityDescriptor(NewSecurityDescriptor
,
491 &Header
->SecurityDescriptor
);
492 DPRINT("Object security descriptor %p\n", Header
->SecurityDescriptor
);
495 /* Release the new security descriptor */
496 SeDeassignSecurity(&NewSecurityDescriptor
);
499 SeReleaseSubjectContext(&SubjectContext
);
503 *Object
= HEADER_TO_BODY(Header
);
506 return STATUS_SUCCESS
;
511 * FUNCTION: Increments the pointer reference count for a given object
513 * ObjectBody = Object's body
514 * DesiredAccess = Desired access to the object
515 * ObjectType = Points to the object type structure
516 * AccessMode = Type of access check to perform
522 ObReferenceObjectByPointer(IN PVOID Object
,
523 IN ACCESS_MASK DesiredAccess
,
524 IN POBJECT_TYPE ObjectType
,
525 IN KPROCESSOR_MODE AccessMode
)
527 POBJECT_HEADER Header
;
529 DPRINT("ObReferenceObjectByPointer(Object %x, ObjectType %x)\n",
532 Header
= BODY_TO_HEADER(Object
);
534 if (ObjectType
!= NULL
&& Header
->ObjectType
!= ObjectType
)
536 DPRINT("Failed %x (type was %x %S) should be %x %S\n",
539 Header
->ObjectType
->TypeName
.Buffer
,
541 ObjectType
->TypeName
.Buffer
);
542 return(STATUS_UNSUCCESSFUL
);
544 if (Header
->ObjectType
== PsProcessType
)
546 DPRINT("Ref p 0x%x refcount %d type %x ",
547 Object
, Header
->RefCount
, PsProcessType
);
548 DPRINT("eip %x\n", ((PULONG
)&Object
)[-1]);
550 if (Header
->ObjectType
== PsThreadType
)
552 DPRINT("Deref t 0x%x with refcount %d type %x ",
553 Object
, Header
->RefCount
, PsThreadType
);
554 DPRINT("eip %x\n", ((PULONG
)&Object
)[-1]);
557 if (Header
->CloseInProcess
)
559 if (Header
->ObjectType
== PsProcessType
)
561 return STATUS_PROCESS_IS_TERMINATING
;
563 if (Header
->ObjectType
== PsThreadType
)
565 return STATUS_THREAD_IS_TERMINATING
;
567 return(STATUS_UNSUCCESSFUL
);
570 InterlockedIncrement(&Header
->RefCount
);
572 return(STATUS_SUCCESS
);
580 ObOpenObjectByPointer(IN POBJECT Object
,
581 IN ULONG HandleAttributes
,
582 IN PACCESS_STATE PassedAccessState
,
583 IN ACCESS_MASK DesiredAccess
,
584 IN POBJECT_TYPE ObjectType
,
585 IN KPROCESSOR_MODE AccessMode
,
590 DPRINT("ObOpenObjectByPointer()\n");
592 Status
= ObReferenceObjectByPointer(Object
,
596 if (!NT_SUCCESS(Status
))
601 Status
= ObCreateHandle(PsGetCurrentProcess(),
604 (BOOLEAN
)(HandleAttributes
& OBJ_INHERIT
),
607 ObDereferenceObject(Object
);
609 return STATUS_SUCCESS
;
614 ObpDeleteObject(POBJECT_HEADER Header
)
616 DPRINT("ObPerformRetentionChecks(Header %p)\n", Header
);
617 if (KeGetCurrentIrql() != PASSIVE_LEVEL
)
619 DPRINT("ObpPerformRetentionChecks called at an unsupported IRQL. Use ObpPerformRetentionChecksDpcLevel instead.\n");
623 if (Header
->SecurityDescriptor
!= NULL
)
625 ObpRemoveSecurityDescriptor(Header
->SecurityDescriptor
);
628 if (Header
->ObjectType
!= NULL
&&
629 Header
->ObjectType
->Delete
!= NULL
)
631 Header
->ObjectType
->Delete(HEADER_TO_BODY(Header
));
634 if (Header
->Name
.Buffer
!= NULL
)
636 ObpRemoveEntryDirectory(Header
);
637 RtlFreeUnicodeString(&Header
->Name
);
640 DPRINT("ObPerformRetentionChecks() = Freeing object\n");
643 return(STATUS_SUCCESS
);
648 ObpDeleteObjectWorkRoutine (IN PVOID Parameter
)
650 PRETENTION_CHECK_PARAMS Params
= (PRETENTION_CHECK_PARAMS
)Parameter
;
651 /* ULONG Tag; */ /* See below */
654 assert(KeGetCurrentIrql() == PASSIVE_LEVEL
); /* We need PAGED_CODE somewhere... */
656 /* Turn this on when we have ExFreePoolWithTag
657 Tag = Params->ObjectHeader->ObjectType->Tag; */
658 ObpDeleteObject(Params
->ObjectHeader
);
660 /* ExFreePoolWithTag(Params, Tag); */
665 ObpDeleteObjectDpcLevel(IN POBJECT_HEADER ObjectHeader
,
668 if (ObjectHeader
->RefCount
< 0)
670 CPRINT("Object %p/%p has invalid reference count (%d)\n",
671 ObjectHeader
, HEADER_TO_BODY(ObjectHeader
),
672 ObjectHeader
->RefCount
);
676 if (ObjectHeader
->HandleCount
< 0)
678 CPRINT("Object %p/%p has invalid handle count (%d)\n",
679 ObjectHeader
, HEADER_TO_BODY(ObjectHeader
),
680 ObjectHeader
->HandleCount
);
684 if (ObjectHeader
->CloseInProcess
)
687 return STATUS_UNSUCCESSFUL
;
689 ObjectHeader
->CloseInProcess
= TRUE
;
691 switch (KeGetCurrentIrql ())
694 return ObpDeleteObject (ObjectHeader
);
699 PRETENTION_CHECK_PARAMS Params
;
702 We use must succeed pool here because if the allocation fails
705 Params
= (PRETENTION_CHECK_PARAMS
)
706 ExAllocatePoolWithTag(NonPagedPoolMustSucceed
,
707 sizeof(RETENTION_CHECK_PARAMS
),
708 ObjectHeader
->ObjectType
->Tag
);
709 Params
->ObjectHeader
= ObjectHeader
;
710 ExInitializeWorkItem(&Params
->WorkItem
,
711 ObpDeleteObjectWorkRoutine
,
713 ExQueueWorkItem(&Params
->WorkItem
,
716 return STATUS_PENDING
;
719 DPRINT("ObpPerformRetentionChecksDpcLevel called at unsupported "
720 "IRQL %u!\n", KeGetCurrentIrql());
722 return STATUS_UNSUCCESSFUL
;
725 return STATUS_SUCCESS
;
729 /**********************************************************************
731 * ObfReferenceObject@4
734 * Increments a given object's reference count and performs
738 * ObjectBody = Body of the object.
746 ObfReferenceObject(IN PVOID Object
)
748 POBJECT_HEADER Header
;
752 Header
= BODY_TO_HEADER(Object
);
754 /* No one should be referencing an object once we are deleting it. */
755 if (Header
->CloseInProcess
)
760 (VOID
)InterlockedIncrement(&Header
->RefCount
);
764 /**********************************************************************
766 * ObfDereferenceObject@4
769 * Decrements a given object's reference count and performs
773 * ObjectBody = Body of the object.
781 ObfDereferenceObject(IN PVOID Object
)
783 POBJECT_HEADER Header
;
790 /* Extract the object header. */
791 Header
= BODY_TO_HEADER(Object
);
792 Permanent
= Header
->Permanent
;
793 HandleCount
= Header
->HandleCount
;
796 Drop our reference and get the new count so we can tell if this was the
799 NewRefCount
= InterlockedDecrement(&Header
->RefCount
);
800 assert(NewRefCount
>= 0);
802 /* Check whether the object can now be deleted. */
803 if (NewRefCount
== 0 &&
807 ObpDeleteObjectDpcLevel(Header
, NewRefCount
);
812 /**********************************************************************
814 * ObGetObjectPointerCount@4
817 * Retrieves the pointer(reference) count of the given object.
820 * ObjectBody = Body of the object.
828 ObGetObjectPointerCount(PVOID Object
)
830 POBJECT_HEADER Header
;
833 Header
= BODY_TO_HEADER(Object
);
835 return(Header
->RefCount
);
839 /**********************************************************************
841 * ObGetObjectHandleCount@4
844 * Retrieves the handle count of the given object.
847 * ObjectBody = Body of the object.
853 ObGetObjectHandleCount(PVOID Object
)
855 POBJECT_HEADER Header
;
858 Header
= BODY_TO_HEADER(Object
);
860 return(Header
->HandleCount
);
864 /**********************************************************************
866 * ObDereferenceObject@4
869 * Decrements a given object's reference count and performs
873 * ObjectBody = Body of the object.
881 #ifdef ObDereferenceObject
882 #undef ObDereferenceObject
886 ObDereferenceObject(IN PVOID Object
)
888 ObfDereferenceObject(Object
);