Windows2000/private/ntos/ob/obref.c
2020-09-30 17:12:32 +02:00

1057 lines
39 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
obref.c
Abstract:
Object open API
Author:
Steve Wood (stevewo) 31-Mar-1989
--*/
#include "obp.h"
// A local variable to tell us if the worker queue is active
BOOLEAN ObpRemoveQueueActive;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,ObGetObjectPointerCount)
#pragma alloc_text(PAGE,ObOpenObjectByName)
#pragma alloc_text(PAGE,ObOpenObjectByPointer)
#pragma alloc_text(PAGE,ObReferenceObjectByName)
#pragma alloc_text(PAGE,ObpRemoveObjectRoutine)
#pragma alloc_text(PAGE,ObpDeleteNameCheck)
#endif
#ifndef LpcpAcquireLpcpLock
// ******************** WARNING ************************
// The following include is neccessary in order to hold the LPC lock
// while setting the port flag to PORT_DELETED.
// A cleanest solution could be a callback on DereferenceObject.
// In that case we'll define the callback for the LPC to do all these things.
#include "..\lpc\lpcp.h"
#endif //#ifndef LpcpAcquireLpcpLock
ULONG ObGetObjectPointerCount(IN PVOID Object)
/*++
Routine Description:
This routine returns the current pointer count for a specified object.
Arguments:
Object - Pointer to the object whose pointer count is to be returned.
Return Value:
The current pointer count for the specified object is returned.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
PAGED_CODE();
// Simply return the current pointer count for the object.
return OBJECT_TO_OBJECT_HEADER(Object)->PointerCount;
}
NTSTATUS
ObOpenObjectByName(
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
IN OUT PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN OUT PVOID ParseContext OPTIONAL,
OUT PHANDLE Handle
)
/*++
Routine Description:
This function opens an object with full access validation and auditing.
Soon after entering we capture the SubjectContext for the caller. This
context must remain captured until auditing is complete, and passed to
any routine that may have to do access checking or auditing.
Arguments:
ObjectAttributes - Supplies a pointer to the object attributes.
ObjectType - Supplies an optional pointer to the object type descriptor.
AccessMode - Supplies the processor mode of the access.
AccessState - Supplies an optional pointer to the current access status
describing already granted access types, the privileges used to get
them, and any access types yet to be granted.
DesiredAcess - Supplies the desired access to the object.
ParseContext - Supplies an optional pointer to parse context.
Handle - Supplies a pointer to a variable that receives the handle value.
Return Value:
If the object is successfully opened, then a handle for the object is
created and a success status is returned. Otherwise, an error status is returned.
--*/
{
NTSTATUS Status;
NTSTATUS HandleStatus;
PVOID ExistingObject;
HANDLE NewHandle;
BOOLEAN DirectoryLocked;
OB_OPEN_REASON OpenReason;
POBJECT_HEADER ObjectHeader;
OBJECT_CREATE_INFORMATION ObjectCreateInfo;
UNICODE_STRING CapturedObjectName;
ACCESS_STATE LocalAccessState;
AUX_ACCESS_DATA AuxData;
PGENERIC_MAPPING GenericMapping;
PAGED_CODE();
ObpValidateIrql("ObOpenObjectByName");
// If the object attributes are not specified, then return an error.
*Handle = NULL;
if (!ARGUMENT_PRESENT(ObjectAttributes)) {
Status = STATUS_INVALID_PARAMETER;
} else {
// Capture the object creation information.
Status = ObpCaptureObjectCreateInformation(ObjectType,
AccessMode,
ObjectAttributes,
&CapturedObjectName,
&ObjectCreateInfo,
TRUE);
// If the object creation information is successfully captured,
// then generate the access state.
if (NT_SUCCESS(Status)) {
if (!ARGUMENT_PRESENT(AccessState)) {
// If an object type descriptor is specified, then use associated generic mapping.
// Otherwise, use no generic mapping.
GenericMapping = NULL;
if (ARGUMENT_PRESENT(ObjectType)) {
GenericMapping = &ObjectType->TypeInfo.GenericMapping;
}
AccessState = &LocalAccessState;
Status = SeCreateAccessState(&LocalAccessState, &AuxData, DesiredAccess, GenericMapping);
if (!NT_SUCCESS(Status)) {
goto FreeCreateInfo;
}
}
// If there is a security descriptor specified in the object
// attributes, then capture it in the access state.
if (ObjectCreateInfo.SecurityDescriptor != NULL) {
AccessState->SecurityDescriptor = ObjectCreateInfo.SecurityDescriptor;
}
// Validate the access state.
Status = ObpValidateAccessMask(AccessState);
// If the access state is valid, then lookup the object by name.
if (NT_SUCCESS(Status)) {
Status = ObpLookupObjectName(ObjectCreateInfo.RootDirectory,
&CapturedObjectName,
ObjectCreateInfo.Attributes,
ObjectType,
AccessMode,
ParseContext,
ObjectCreateInfo.SecurityQos,
NULL,
AccessState,
&DirectoryLocked,
&ExistingObject);
// If the object was successfully looked up, then attempt
// to create or open a handle.
if (NT_SUCCESS(Status)) {
ObjectHeader = OBJECT_TO_OBJECT_HEADER(ExistingObject);
// If the object is being created, then the operation
// must be a open-if operation. Otherwise, a handle to
// an object is being opened.
if (ObjectHeader->Flags & OB_FLAG_NEW_OBJECT) {
OpenReason = ObCreateHandle;
if (ObjectHeader->ObjectCreateInfo != NULL) {
ObpFreeObjectCreateInformation(ObjectHeader->ObjectCreateInfo);
ObjectHeader->ObjectCreateInfo = NULL;
}
} else {
OpenReason = ObOpenHandle;
}
// If any of the object attributes are invalid, then
// return an error status.
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & ObjectCreateInfo.Attributes) {
Status = STATUS_INVALID_PARAMETER;
if (DirectoryLocked) {
ObpLeaveRootDirectoryMutex();
}
} else {
// The status returned by the object lookup routine
// must be returned if the creation of a handle is
// successful. Otherwise, the handle creation status
// is returned.
HandleStatus = ObpCreateHandle(OpenReason,
ExistingObject,
ObjectType,
AccessState,
0,
ObjectCreateInfo.Attributes,
DirectoryLocked,
AccessMode,
(PVOID *)NULL,
&NewHandle);
if (!NT_SUCCESS(HandleStatus)) {
ObDereferenceObject(ExistingObject);
Status = HandleStatus;
} else {
*Handle = NewHandle;
}
}
} else {
if (DirectoryLocked) {
ObpLeaveRootDirectoryMutex();
}
}
}
// If the access state was generated, then delete the access state.
if (AccessState == &LocalAccessState) {
SeDeleteAccessState(AccessState);
}
// Free the create information.
FreeCreateInfo:
ObpReleaseObjectCreateInformation(&ObjectCreateInfo);
if (CapturedObjectName.Buffer != NULL) {
ObpFreeObjectNameBuffer(&CapturedObjectName);
}
}
}
return Status;
}
NTSTATUS ObOpenObjectByPointer(
IN PVOID Object,
IN ULONG HandleAttributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
OUT PHANDLE Handle)
/*++
Routine Description:
This routine opens an object referenced by a pointer.
Arguments:
Object - A pointer to the object being opened.
HandleAttributes - The desired attributes for the handle, such
as OBJ_INHERIT, OBJ_PERMANENT, OBJ_EXCLUSIVE, OBJ_CASE_INSENSITIVE, OBJ_OPENIF, and OBJ_OPENLINK
PassedAccessState - Supplies an optional pointer to the current access
status describing already granted access types, the privileges used to get them, and any access types yet to be granted.
DesiredAcess - Supplies the desired access to the object.
ObjectType - Supplies the type of the object being opened
AccessMode - Supplies the processor mode of the access.
Handle - Supplies a pointer to a variable that receives the handle value.
Return Value:
An appropriate NTSTATUS value
--*/
{
NTSTATUS Status;
HANDLE NewHandle;
POBJECT_HEADER ObjectHeader;
ACCESS_STATE LocalAccessState;
PACCESS_STATE AccessState = NULL;
AUX_ACCESS_DATA AuxData;
PAGED_CODE();
ObpValidateIrql("ObOpenObjectByPointer");
// First increment the pointer count for the object. This routine
// also checks the object types
Status = ObReferenceObjectByPointer(Object, 0, ObjectType, AccessMode);
if (NT_SUCCESS(Status)) {
// Get the object header for the input object body
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
// If the caller did not pass in an access state then
// we will create a new one based on the desired access and the object types generic mapping
if (!ARGUMENT_PRESENT(PassedAccessState)) {
Status = SeCreateAccessState(&LocalAccessState, &AuxData, DesiredAccess, &ObjectHeader->Type->TypeInfo.GenericMapping);
if (!NT_SUCCESS(Status)) {
ObDereferenceObject(Object);
return(Status);
}
AccessState = &LocalAccessState;
// Otherwise the caller did specify an access state so we use the one passed in.
} else {
AccessState = PassedAccessState;
}
// Make sure the caller is asking for handle attributes that are
// valid for the given object type
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & HandleAttributes) {
if (AccessState == &LocalAccessState) {
SeDeleteAccessState(AccessState);
}
ObDereferenceObject(Object);
return(STATUS_INVALID_PARAMETER);
}
// We've referenced the object and have an access state to give
// the new handle so now create a new handle for the object.
Status = ObpCreateHandle(ObOpenHandle,
Object,
ObjectType,
AccessState,
0,
HandleAttributes,
FALSE,
AccessMode,
(PVOID *)NULL,
&NewHandle);
if (!NT_SUCCESS(Status)) {
ObDereferenceObject(Object);
}
}
// If we successfully opened by object and created a new handle
// then set the output variable correctly
if (NT_SUCCESS(Status)) {
*Handle = NewHandle;
} else {
*Handle = NULL;
}
// Check if we used our own access state and now need to cleanup
if (AccessState == &LocalAccessState) {
SeDeleteAccessState(AccessState);
}
return(Status);// And return to our caller
}
NTSTATUS
ObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
)
/*++
Routine Description:
Given a handle to an object this routine returns a pointer
to the body of the object with proper ref counts
Arguments:
Handle - Supplies a handle to the object being referenced. It can
also be the result of NtCurrentProcess or NtCurrentThread
DesiredAccess - Supplies the access being requested by the caller
ObjectType - Optionally supplies the type of the object we are expecting
AccessMode - Supplies the processor mode of the access
Object - Receives a pointer to the object body if the operation is successful
HandleInformation - Optionally receives information regarding the input handle.
Return Value:
An appropriate NTSTATUS value
--*/
{
ACCESS_MASK GrantedAccess;
PHANDLE_TABLE HandleTable;
POBJECT_HEADER ObjectHeader;
PHANDLE_TABLE_ENTRY ObjectTableEntry;
PEPROCESS Process;
NTSTATUS Status;
PETHREAD Thread;
BOOLEAN AttachedToProcess = FALSE;
ObpValidateIrql("ObReferenceObjectByHandle");
// Protect ourselves from being interrupted while we hold a handle table entry lock
KeEnterCriticalRegion();
try {
// If the handle is equal to the current process handle and the object
// type is NULL or type process, then attempt to translate a handle to the current process.
// Otherwise, check if the handle is the current thread handle.
if (Handle == NtCurrentProcess()) {
if ((ObjectType == PsProcessType) || (ObjectType == NULL)) {
Process = PsGetCurrentProcess();
GrantedAccess = Process->GrantedAccess;
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) || (AccessMode == KernelMode)) {
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);
if (ARGUMENT_PRESENT(HandleInformation)) {
HandleInformation->GrantedAccess = GrantedAccess;
HandleInformation->HandleAttributes = 0;
}
ObpIncrPointerCount(ObjectHeader);
*Object = Process;
ASSERT(*Object != NULL);
Status = STATUS_SUCCESS;
leave;
} else {
Status = STATUS_ACCESS_DENIED;
}
} else {
Status = STATUS_OBJECT_TYPE_MISMATCH;
}
// If the handle is equal to the current thread handle and the object
// type is NULL or type thread, then attempt to translate a handle to
// the current thread. Otherwise, the we'll try and translate the handle
} else if (Handle == NtCurrentThread()) {
if ((ObjectType == PsThreadType) || (ObjectType == NULL)) {
Thread = PsGetCurrentThread();
GrantedAccess = Thread->GrantedAccess;
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) || (AccessMode == KernelMode)) {
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);
if (ARGUMENT_PRESENT(HandleInformation)) {
HandleInformation->GrantedAccess = GrantedAccess;
HandleInformation->HandleAttributes = 0;
}
ObpIncrPointerCount(ObjectHeader);
*Object = Thread;
ASSERT(*Object != NULL);
Status = STATUS_SUCCESS;
leave;
} else {
Status = STATUS_ACCESS_DENIED;
}
} else {
Status = STATUS_OBJECT_TYPE_MISMATCH;
}
// Otherwise the handle is not a built in value. It must be an index into a handle table.
} else {
#if DBG
// On checked builds, check that if the Kernel handle bit is set,
// then we're coming from Kernel mode. We should probably fail the
// call if bit set && !Kmode
// We know we're NOT a builtin handle at this point
ASSERT((Handle < 0) ? (AccessMode == KernelMode) : TRUE);
#endif
// First get a pointer to either the processes handle table or the
// global kernel handle table. If it is the kernel handle then we
// need to clear the handle bit and attach to the system process
if (IsKernelHandle(Handle, AccessMode)) {
Handle = DecodeKernelHandle(Handle);// Make the handle look like a regular handle
HandleTable = ObpKernelHandleTable;// The global kernel handle table
} else {
HandleTable = ObpGetObjectTable();
}
ASSERT(HandleTable != NULL);
// Translate the specified handle to an object table index.
ObjectTableEntry = ExMapHandleToPointer(HandleTable, Handle);
// Make sure the object table entry really does exist
if (ObjectTableEntry != NULL) {
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
if ((ObjectHeader->Type == ObjectType) || (ObjectType == NULL)) {
#if i386 && !FPO
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
if ((AccessMode != KernelMode) || ARGUMENT_PRESENT(HandleInformation)) {
GrantedAccess = ObpTranslateGrantedAccessIndex(ObjectTableEntry->GrantedAccessIndex);
}
} else {
GrantedAccess = ObjectTableEntry->GrantedAccess;
}
#else
GrantedAccess = ObjectTableEntry->GrantedAccess;
#endif // i386 && !FPO
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) || (AccessMode == KernelMode)) {
// Access to the object is allowed. Return the handle
// information is requested, increment the object
// pointer count, unlock the handle table and return a success status.
// Note that this is the only successful return path
// out of this routine if the user did not specify
// the current process or current thread in the input handle.
if (ARGUMENT_PRESENT(HandleInformation)) {
HandleInformation->GrantedAccess = GrantedAccess;
HandleInformation->HandleAttributes = ObjectTableEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES;
}
ObpIncrPointerCount(ObjectHeader);
*Object = &ObjectHeader->Body;
ASSERT(*Object != NULL);
ExUnlockHandleTableEntry(HandleTable, ObjectTableEntry);
Status = STATUS_SUCCESS;
leave;
} else {
Status = STATUS_ACCESS_DENIED;
}
} else {
Status = STATUS_OBJECT_TYPE_MISMATCH;
}
ExUnlockHandleTableEntry(HandleTable, ObjectTableEntry);
} else {
Status = STATUS_INVALID_HANDLE;
}
}
// If we are attached to the system process then return
// back to our caller
if (AttachedToProcess) {
KeDetachProcess();
}
// No handle translation is possible. Set the object address to NULL
// and return an error status.
*Object = NULL;
} finally{
KeLeaveCriticalRegion();
}
return Status;
}
NTSTATUS
ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *Object
)
/*++
Routine Description:
Given a name of an object this routine returns a pointer
to the body of the object with proper ref counts
Arguments:
ObjectName - Supplies the name of the object being referenced
Attributes - Supplies the desired handle attributes
AccessState - Supplies an optional pointer to the current access
status describing already granted access types, the privileges used
to get them, and any access types yet to be granted.
DesiredAccess - Optionally supplies the desired access to the for the object
ObjectType - Specifies the object type according to the caller
AccessMode - Supplies the processor mode of the access
ParseContext - Optionally supplies a context to pass down to the parse routine
Object - Receives a pointer to the referenced object body
Return Value:
An appropriate NTSTATUS value
--*/
{
UNICODE_STRING CapturedObjectName;
BOOLEAN DirectoryLocked;
PVOID ExistingObject;
ACCESS_STATE LocalAccessState;
AUX_ACCESS_DATA AuxData;
NTSTATUS Status;
PAGED_CODE();
ObpValidateIrql("ObReferenceObjectByName");
// If the object name descriptor is not specified, or the object name
// length is zero (tested after capture), then the object name is invalid.
if (ObjectName == NULL) {
return STATUS_OBJECT_NAME_INVALID;
}
// Capture the object name.
Status = ObpCaptureObjectName(AccessMode, ObjectName, &CapturedObjectName, TRUE);
if (NT_SUCCESS(Status)) {
// No buffer has been allocated for a zero length name so no free needed
if (CapturedObjectName.Length == 0) {
return STATUS_OBJECT_NAME_INVALID;
}
// If the access state is not specified, then create the access state.
if (!ARGUMENT_PRESENT(AccessState)) {
AccessState = &LocalAccessState;
Status = SeCreateAccessState(&LocalAccessState,
&AuxData,
DesiredAccess,
&ObjectType->TypeInfo.GenericMapping);
if (!NT_SUCCESS(Status)) {
goto FreeBuffer;
}
}
// Lookup object by name.
Status = ObpLookupObjectName(NULL,
&CapturedObjectName,
Attributes,
ObjectType,
AccessMode,
ParseContext,
NULL,
NULL,
AccessState,
&DirectoryLocked,
&ExistingObject);
// If the directory is returned locked, then unlock it.
if (DirectoryLocked) {
ObpLeaveRootDirectoryMutex();
}
// If the lookup was successful, then return the existing
// object if access is allowed. Otherwise, return NULL.
*Object = NULL;
if (NT_SUCCESS(Status)) {
if (ObpCheckObjectReference(ExistingObject, AccessState, FALSE, AccessMode, &Status)) {
*Object = ExistingObject;
}
}
// If the access state was generated, then delete the access state.
if (AccessState == &LocalAccessState) {
SeDeleteAccessState(AccessState);
}
// Free the object name buffer.
FreeBuffer:
ObpFreeObjectNameBuffer(&CapturedObjectName);
}
return Status;
}
NTSTATUS
ObReferenceObjectByPointer(
IN PVOID Object,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode
)
/*++
Routine Description:
This routine adds another reference count to an object denoted by
a pointer to the object body
Arguments:
Object - Supplies a pointer to the object being referenced
DesiredAccess - Specifies the desired access for the reference
ObjectType - Specifies the object type according to the caller
AccessMode - Supplies the processor mode of the access
Return Value:
STATUS_SUCCESS if successful and STATUS_OBJECT_TYPE_MISMATCH otherwise
--*/
{
POBJECT_HEADER ObjectHeader;
// Translate the pointer to the object body to a pointer to the object header
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
// If the specified object type does not match and either the caller is
// not kernel mode or it is not a symbolic link object then it is an error
if ((ObjectHeader->Type != ObjectType) && (AccessMode != KernelMode || ObjectType == ObpSymbolicLinkObjectType)) {
return(STATUS_OBJECT_TYPE_MISMATCH);
}
// Otherwise increment the pointer count and return success to our caller
ObpIncrPointerCount(ObjectHeader);
return(STATUS_SUCCESS);
}
VOID FASTCALL ObfReferenceObject(IN PVOID Object)
/*++
Routine Description:
This function increments the reference count for an object.
N.B. This function should be used to increment the reference count
when the accessing mode is kernel or the objct type is known.
Arguments:
Object - Supplies a pointer to the object whose reference count is incremented.
Return Value:
None.
--*/
{
POBJECT_HEADER ObjectHeader;
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
ObpIncrPointerCount(ObjectHeader);
return;
}
VOID FASTCALL ObfDereferenceObject(IN PVOID Object)
/*++
Routine Description:
This routine decrments the refernce count of the specified object and
does whatever cleanup there is if the count goes to zero.
Arguments:
Object - Supplies a pointer to the body of the object being dereferenced
Return Value:
None.
--*/
{
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
KIRQL OldIrql;
BOOLEAN StartWorkerThread;
PLPCP_PORT_OBJECT Port = NULL;
#if DBG
POBJECT_HEADER_NAME_INFO NameInfo;
#endif
// Translate a pointer to the object body to a pointer to the object header.
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
#if DBG
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
if (NameInfo) {
InterlockedDecrement(&NameInfo->DbgDereferenceCount);
}
#endif
// Decrement the point count and if the result is now then
// there is extra work to do
ObjectType = ObjectHeader->Type;
if ((ObjectType == LpcPortObjectType) || (ObjectType == LpcWaitablePortObjectType)) {
Port = Object;
LpcpAcquireLpcpLock();
}
if (ObpDecrPointerCountWithResult(ObjectHeader)) {
// Find out the level we're at and the object type
OldIrql = KeGetCurrentIrql();
ASSERT(ObjectHeader->HandleCount == 0);
if (Port != NULL) {
Port->Flags |= PORT_DELETED;
Port = NULL;
LpcpReleaseLpcpLock();
}
// If we're at the passive level then go ahead and delete the
// object now. Or if we're at APC level and object say's we're
// allocated out of paged pool then also go ahead and delete
// the object right now.
if ((OldIrql == PASSIVE_LEVEL) ||
((OldIrql == APC_LEVEL) &&
((ObjectType != NULL) && (ObjectType->TypeInfo.PoolType != NonPagedPool)))) {
ObpRemoveObjectRoutine(Object);
return;
} else {
// Objects can't be deleted from an IRQL above APC_LEVEL.
// Nonpaged objects can't be deleted from APC_LEVEL.
// So queue the delete operation.
ASSERT((ObjectHeader->Type == NULL) || (ObjectHeader->Type->TypeInfo.PoolType == NonPagedPool));
// Lock the work queue, enqueue the new work item, and if the
// work queue is not active then make it active, indicate that
// we need to start the worker thread, and unlock the work queue.
ExAcquireSpinLock(&ObpLock, &OldIrql);
PushEntryList((PSINGLE_LIST_ENTRY)&ObpRemoveObjectQueue, (PSINGLE_LIST_ENTRY)&ObjectHeader->SEntry);
if (!ObpRemoveQueueActive) {
ObpRemoveQueueActive = TRUE;
StartWorkerThread = TRUE;
} else {
StartWorkerThread = FALSE;
}
#if 0
if (StartWorkerThread) {
KdPrint(("OB: %08x Starting ObpProcessRemoveObjectQueue thread.\n", Object));
} else {
KdPrint(("OB: %08x Queued to ObpProcessRemoveObjectQueue thread.\n", Object));
}
#endif // 1
ExReleaseSpinLock(&ObpLock, OldIrql);
// If we have to start the worker thread then go ahead and enqueue the work item
if (StartWorkerThread) {
ExInitializeWorkItem(&ObpRemoveObjectWorkItem, ObpProcessRemoveObjectQueue, NULL);
ExQueueWorkItem(&ObpRemoveObjectWorkItem, CriticalWorkQueue);
}
}
}
if (Port != NULL) {
LpcpReleaseLpcpLock();
}
}
VOID ObpProcessRemoveObjectQueue(PVOID Parameter)
/*++
Routine Description:
This is the work routine for the remove object work queue.
Its job is to remove and process items from the remove object queue.
Arguments:
Parameter - Ignored
Return Value:
None.
--*/
{
PSINGLE_LIST_ENTRY Entry;
POBJECT_HEADER ObjectHeader;
KIRQL OldIrql;
// Lock the work queue this will keep the preceding routine
// from monkeying with the queue
ExAcquireSpinLock(&ObpLock, &OldIrql);
// While there are items in our private remove object work queue
// then we remove each item, get back up to the object header,
// and delete the object. The latter part done outside of the
// work queue lock
Entry = PopEntryList((PSINGLE_LIST_ENTRY)&ObpRemoveObjectQueue);
while (Entry != NULL) {
ExReleaseSpinLock(&ObpLock, OldIrql);
ObjectHeader = CONTAINING_RECORD(Entry, OBJECT_HEADER, SEntry);
ObpRemoveObjectRoutine(&ObjectHeader->Body);
ExAcquireSpinLock(&ObpLock, &OldIrql);
Entry = PopEntryList((PSINGLE_LIST_ENTRY)&ObpRemoveObjectQueue);
}
// Indicate that we are now going inactive and then unlock the work queue
ObpRemoveQueueActive = FALSE;
ExReleaseSpinLock(&ObpLock, OldIrql);
}
VOID ObpRemoveObjectRoutine(PVOID Object)
/*++
Routine Description:
This routine is used to delete an object whose reference count has gone to zero.
Arguments:
Object - Supplies a pointer to the body of the object being deleted
Return Value:
None.
--*/
{
NTSTATUS Status;
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
POBJECT_HEADER_NAME_INFO NameInfo;
PAGED_CODE();
ObpValidateIrql("ObpRemoveObjectRoutine");
// Retrieve an object header from the object body, and also get
// the object type, creator and name info if available
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
ObjectType = ObjectHeader->Type;
CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(ObjectHeader);
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
// Get exclusive access to the object type object
ObpEnterObjectTypeMutex(ObjectType);
// If there is a creator info record and we are on the list
// for the object type then remove this object from the list
if (CreatorInfo != NULL && !IsListEmpty(&CreatorInfo->TypeList)) {
RemoveEntryList(&CreatorInfo->TypeList);
}
// If there is a name info record and the name buffer is not null
// then free the buffer and zero out the name record
if (NameInfo != NULL && NameInfo->Name.Buffer != NULL) {
ExFreePool(NameInfo->Name.Buffer);
NameInfo->Name.Buffer = NULL;
NameInfo->Name.Length = 0;
NameInfo->Name.MaximumLength = 0;
}
// We are done with the object type object so we can now release it
ObpLeaveObjectTypeMutex(ObjectType);
// Security descriptor deletion must precede the
// call to the object's DeleteProcedure. Check if we have
// a security descriptor and if so then call the routine
// to delete the security descritpor.
if (ObjectHeader->SecurityDescriptor != NULL) {
KIRQL SaveIrql;
ObpBeginTypeSpecificCallOut(SaveIrql);
Status = (ObjectType->TypeInfo.SecurityProcedure)(Object,
DeleteSecurityDescriptor,
NULL, NULL, NULL,
&ObjectHeader->SecurityDescriptor,
0,
NULL);
ObpEndTypeSpecificCallOut(SaveIrql, "Security", ObjectType, Object);
}
// Now if there is a delete callback for the object type invoke the routine
if (ObjectType->TypeInfo.DeleteProcedure) {
KIRQL SaveIrql;
ObpBeginTypeSpecificCallOut(SaveIrql);
(*(ObjectType->TypeInfo.DeleteProcedure))(Object);
ObpEndTypeSpecificCallOut(SaveIrql, "Delete", ObjectType, Object);
}
// Finally return the object back to pool including releasing any quota charges
ObpFreeObject(Object);
}
VOID ObpDeleteNameCheck(IN PVOID Object, IN BOOLEAN TypeMutexHeld)
/*++
Routine Description:
This routine removes the name of an object from its parent directory
Arguments:
Object - Supplies a pointer to the object body whose name is being checked
TypeMutexHeld - Indicates if the lock on object type is being held by the caller
Return Value:
None.
--*/
{
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
POBJECT_HEADER_NAME_INFO NameInfo;
PVOID DirObject;
PAGED_CODE();
ObpValidateIrql("ObpDeleteNameCheck");
// Translate the object body to an object header also get
// the object type and name info if present
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
ObjectType = ObjectHeader->Type;
// If the lock is not held then get the lock now
if (!TypeMutexHeld) {
ObpEnterObjectTypeMutex(ObjectType);
}
// Make sure that the object has a zero handle count, has a non
// empty name buffer, and is not a permanent object
if ((ObjectHeader->HandleCount == 0) &&
(NameInfo != NULL) &&
(NameInfo->Name.Length != 0) &&
(!(ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT))) {
// Give up the lock on the object type and instead
// get the lock on the object directories
ObpLeaveObjectTypeMutex(ObjectType);
ObpEnterRootDirectoryMutex();
DirObject = NULL;
// Check that the object we is still in the directory otherwise
// then is nothing for us to remove
if (Object == ObpLookupDirectoryEntry(NameInfo->Directory, &NameInfo->Name, 0)) {
// Now reacquire the lock on the object type and
// check check the handle count again. If it is still
// zero then we can do the actual delete name operation
ObpEnterObjectTypeMutex(ObjectType);
if (ObjectHeader->HandleCount == 0) {
KIRQL SaveIrql;
// Delete the directory entry
ObpDeleteDirectoryEntry(NameInfo->Directory);
// If security is not required invoke the
// security callback to delete the security descriptor
if (!ObjectType->TypeInfo.SecurityRequired) {
ObpBeginTypeSpecificCallOut(SaveIrql);
(ObjectType->TypeInfo.SecurityProcedure)(Object,
DeleteSecurityDescriptor,
NULL,
NULL,
NULL,
&ObjectHeader->SecurityDescriptor,
ObjectType->TypeInfo.PoolType,
NULL);
ObpEndTypeSpecificCallOut(SaveIrql, "Security", ObjectType, Object);
}
// If this is a symbolic link object then we also need to
// delete the symbolic link
if (ObjectType == ObpSymbolicLinkObjectType) {
ObpDeleteSymbolicLinkName((POBJECT_SYMBOLIC_LINK)Object);
}
// Free the name buffer and zero out the name data fields
ExFreePool(NameInfo->Name.Buffer);
NameInfo->Name.Buffer = NULL;
NameInfo->Name.Length = 0;
NameInfo->Name.MaximumLength = 0;
DirObject = NameInfo->Directory;
NameInfo->Directory = NULL;
}
ObpLeaveObjectTypeMutex(ObjectType);
}
ObpLeaveRootDirectoryMutex();
// If there is a directory object for the name then decrement
// its reference count for it and for the object
if (DirObject != NULL) {
ObDereferenceObject(DirObject);
ObDereferenceObject(Object);
}
} else {
// Otherwise don't do any work but simply release the object type
// lock and return to our caller
ObpLeaveObjectTypeMutex(ObjectType);
}
}
// Thunks to support standard call callers
#ifdef ObDereferenceObject
#undef ObDereferenceObject
#endif
VOID ObDereferenceObject(IN PVOID Object)
/*++
Routine Description:
This is really just a thunk for the Obf version of the dereference routine
Arguments:
Object - Supplies a pointer to the body of the object being dereferenced
Return Value:
None.
--*/
{
ObfDereferenceObject(Object);
}