/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    obquery.c

Abstract:

    Query Object system service

--*/

#include "obp.h"

//
//  Local procedure prototypes
//

//
//  The following structure is used to pass the call back routine
//  "ObpSetHandleAttributes" the captured object information and
//  the processor mode of the caller.
//

typedef struct __OBP_SET_HANDLE_ATTRIBUTES {
    OBJECT_HANDLE_FLAG_INFORMATION ObjectInformation;
    KPROCESSOR_MODE PreviousMode;
} OBP_SET_HANDLE_ATTRIBUTES, *POBP_SET_HANDLE_ATTRIBUTES;

BOOLEAN
ObpSetHandleAttributes (
    IN OUT PVOID TableEntry,
    IN ULONG_PTR Parameter
    );

#pragma alloc_text(PAGE, NtQueryObject)
#pragma alloc_text(PAGE, ObpQueryNameString)
#pragma alloc_text(PAGE, ObQueryNameString)
#pragma alloc_text(PAGE, ObQueryTypeName)
#pragma alloc_text(PAGE, ObQueryTypeInfo)
#pragma alloc_text(PAGE, ObQueryObjectAuditingByHandle)
#pragma alloc_text(PAGE, NtSetInformationObject)
#pragma alloc_text(PAGE, ObpSetHandleAttributes)
#pragma alloc_text(PAGE, ObSetHandleAttributes)

NTSTATUS
NtQueryObject (
    __in HANDLE Handle,
    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
    __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
    __in ULONG ObjectInformationLength,
    __out_opt PULONG ReturnLength
    )

/*++

Routine description:

    This routine is used to query information about a given object

Arguments:

    Handle - Supplies a handle to the object being queried.  This value
        is ignored if the requested information class is for type
        information.

    ObjectInformationClass - Specifies the type of information to return

    ObjectInformation - Supplies an output buffer for the information being
        returned

    ObjectInformationLength - Specifies, in bytes, the length of the
        preceding object information buffer

    ReturnLength - Optionally receives the length, in bytes, used to store
        the object information

Return Value:

    An appropriate status value

--*/

{
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;
    PVOID Object;
    POBJECT_HEADER ObjectHeader;
    POBJECT_HEADER_QUOTA_INFO QuotaInfo;
    POBJECT_HEADER_NAME_INFO NameInfo;
    POBJECT_TYPE ObjectType;
    POBJECT_HEADER ObjectDirectoryHeader;
    POBJECT_DIRECTORY ObjectDirectory;
    ACCESS_MASK GrantedAccess;
    POBJECT_HANDLE_FLAG_INFORMATION HandleFlags;
    OBJECT_HANDLE_INFORMATION HandleInformation = {0};
    ULONG NameInfoSize;
    ULONG SecurityDescriptorSize;
    ULONG TempReturnLength;
    OBJECT_BASIC_INFORMATION ObjectBasicInfo;
    POBJECT_TYPES_INFORMATION TypesInformation;
    POBJECT_TYPE_INFORMATION TypeInfo;
    ULONG i;

    PAGED_CODE();

    //
    //  Initialize our local variables
    //

    TempReturnLength = 0;

    //
    //  Get previous processor mode and probe output argument if necessary.
    //

    PreviousMode = KeGetPreviousMode();

    if (PreviousMode != KernelMode) {

        try {

            if (ObjectInformationClass != ObjectHandleFlagInformation) {

                ProbeForWrite( ObjectInformation,
                               ObjectInformationLength,
                               sizeof( ULONG ));

            } else {

                ProbeForWrite( ObjectInformation,
                               ObjectInformationLength,
                               1 );
            }

            //
            //  We'll use a local temp return length variable to pass
            //  through to the later ob query calls which will increment
            //  its value.  We can't pass the users return length directly
            //  because the user might also be altering its value behind
            //  our back.
            //

            if (ARGUMENT_PRESENT( ReturnLength )) {

                ProbeForWriteUlong( ReturnLength );
            }

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            return( GetExceptionCode() );
        }
    }

    //
    //  If the query is not for types information then we
    //  will have to get the object in question. Otherwise
    //  for types information there really isn't an object
    //  to grab.
    //

    if (ObjectInformationClass != ObjectTypesInformation) {

        Status = ObReferenceObjectByHandle( Handle,
                                            0,
                                            NULL,
                                            PreviousMode,
                                            &Object,
                                            &HandleInformation );

        if (!NT_SUCCESS( Status )) {

            return( Status );
        }

        GrantedAccess = HandleInformation.GrantedAccess;

        ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
        ObjectType = ObjectHeader->Type;

    } else {

        GrantedAccess = 0;
        Object = NULL;
        ObjectHeader = NULL;
        ObjectType = NULL;
        Status = STATUS_SUCCESS;
    }

    //
    //  Now process the particular information class being
    //  requested
    //

    switch( ObjectInformationClass ) {

    case ObjectBasicInformation:

        //
        //  Make sure the output buffer is long enough and then
        //  fill in the appropriate fields into our local copy
        //  of basic information.
        //

        if (ObjectInformationLength != sizeof( OBJECT_BASIC_INFORMATION )) {

            ObDereferenceObject( Object );

            return( STATUS_INFO_LENGTH_MISMATCH );
        }

        ObjectBasicInfo.Attributes = HandleInformation.HandleAttributes;

        if (ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT) {

            ObjectBasicInfo.Attributes |= OBJ_PERMANENT;
        }

        if (ObjectHeader->Flags & OB_FLAG_EXCLUSIVE_OBJECT) {

            ObjectBasicInfo.Attributes |= OBJ_EXCLUSIVE;
        }

        ObjectBasicInfo.GrantedAccess = GrantedAccess;
        ObjectBasicInfo.HandleCount = (ULONG)ObjectHeader->HandleCount;
        ObjectBasicInfo.PointerCount = (ULONG)ObjectHeader->PointerCount;

        QuotaInfo = OBJECT_HEADER_TO_QUOTA_INFO( ObjectHeader );

        if (QuotaInfo != NULL) {

            ObjectBasicInfo.PagedPoolCharge = QuotaInfo->PagedPoolCharge;
            ObjectBasicInfo.NonPagedPoolCharge = QuotaInfo->NonPagedPoolCharge;

        } else {

            ObjectBasicInfo.PagedPoolCharge = 0;
            ObjectBasicInfo.NonPagedPoolCharge = 0;
        }

        if (ObjectType == ObpSymbolicLinkObjectType) {

            ObjectBasicInfo.CreationTime = ((POBJECT_SYMBOLIC_LINK)Object)->CreationTime;

        } else {

            RtlZeroMemory( &ObjectBasicInfo.CreationTime,
                           sizeof( ObjectBasicInfo.CreationTime ));
        }

        //
        //  Compute the size of the object name string by taking its name plus
        //  separators and traversing up to the root adding each directories
        //  name length plus separators
        //

        NameInfo = ObpReferenceNameInfo( ObjectHeader );

        if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {

            PVOID ReferencedDirectory = NULL;
        
            //
            //  We grab the root directory lock and test again the directory
            //

            ObjectDirectory = NameInfo->Directory;

            ASSERT (ObjectDirectory);

            ObfReferenceObject( ObjectDirectory );
            ReferencedDirectory = ObjectDirectory;

            NameInfoSize = sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;

            ObpDereferenceNameInfo( NameInfo );
            NameInfo = NULL;

            while (ObjectDirectory) {

                ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory );
                NameInfo = ObpReferenceNameInfo( ObjectDirectoryHeader );

                if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {

                    NameInfoSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;
                        
                    ObjectDirectory = NameInfo->Directory;

                    ObfReferenceObject( ObjectDirectory );
                        
                    ObpDereferenceNameInfo( NameInfo );
                    NameInfo = NULL;
                    ObDereferenceObject( ReferencedDirectory );
                    ReferencedDirectory = ObjectDirectory;

                } else {

                    break;
                }
            }

            if (ReferencedDirectory) {

                ObDereferenceObject( ReferencedDirectory );
            }

            NameInfoSize += sizeof( OBJECT_NAME_INFORMATION ) + sizeof( UNICODE_NULL );

        } else {

            NameInfoSize = 0;
        }

        ObpDereferenceNameInfo( NameInfo );
        NameInfo = NULL;

        ObjectBasicInfo.NameInfoSize = NameInfoSize;
        ObjectBasicInfo.TypeInfoSize = ObjectType->Name.Length + sizeof( UNICODE_NULL ) +
                                        sizeof( OBJECT_TYPE_INFORMATION );
        
        if ((GrantedAccess & READ_CONTROL) &&
            ARGUMENT_PRESENT( ObjectHeader->SecurityDescriptor )) {

            SECURITY_INFORMATION SecurityInformation;

            //
            //  Request a complete security descriptor
            //

            SecurityInformation = OWNER_SECURITY_INFORMATION |
                                  GROUP_SECURITY_INFORMATION |
                                  DACL_SECURITY_INFORMATION  |
                                  SACL_SECURITY_INFORMATION;
            
            SecurityDescriptorSize = 0;

            (ObjectType->TypeInfo.SecurityProcedure)( Object,
                                                      QuerySecurityDescriptor,
                                                      &SecurityInformation,
                                                      NULL,
                                                      &SecurityDescriptorSize,
                                                      &ObjectHeader->SecurityDescriptor,
                                                      ObjectType->TypeInfo.PoolType,
                                                      &ObjectType->TypeInfo.GenericMapping );

        } else {

            SecurityDescriptorSize = 0;
        }

        ObjectBasicInfo.SecurityDescriptorSize = SecurityDescriptorSize;

        //
        //  Now that we've packaged up our local copy of basic info we need
        //  to copy it into the output buffer and set the return
        //  length
        //

        try {

            *(POBJECT_BASIC_INFORMATION) ObjectInformation = ObjectBasicInfo;

            TempReturnLength = ObjectInformationLength;

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            //
            // Fall through, since we cannot undo what we have done.
            //
        }

        break;

    case ObjectNameInformation:

        //
        //  Call a local worker routine
        //

        Status = ObpQueryNameString( Object,
                                     (POBJECT_NAME_INFORMATION)ObjectInformation,
                                     ObjectInformationLength,
                                     &TempReturnLength,
                                     PreviousMode );
        break;

    case ObjectTypeInformation:

        //
        //  Call a local worker routine
        //

        Status = ObQueryTypeInfo( ObjectType,
                                  (POBJECT_TYPE_INFORMATION)ObjectInformation,
                                  ObjectInformationLength,
                                  &TempReturnLength );
        break;

    case ObjectTypesInformation:

        try {

            //
            //  The first thing we do is set the return length to cover the
            //  types info record.  Later in each call to query type info
            //  this value will be updated as necessary
            //

            TempReturnLength = sizeof( OBJECT_TYPES_INFORMATION );

            //
            //  Make sure there is enough room to hold the types info record
            //  and if so then compute the number of defined types there are
            //

            TypesInformation = (POBJECT_TYPES_INFORMATION)ObjectInformation;

            if (ObjectInformationLength < sizeof( OBJECT_TYPES_INFORMATION ) ) {

                Status = STATUS_INFO_LENGTH_MISMATCH;

            } else {

                TypesInformation->NumberOfTypes = 0;

                for (i=0; i<OBP_MAX_DEFINED_OBJECT_TYPES; i++) {

                    ObjectType = ObpObjectTypes[ i ];

                    if (ObjectType == NULL) {

                        break;
                    }

                    TypesInformation->NumberOfTypes += 1;
                }
            }

            //
            //  For each defined type we will query the type info for the
            //  object type and adjust the TypeInfo pointer to the next
            //  free spot
            //

            TypeInfo = (POBJECT_TYPE_INFORMATION)(((PUCHAR)TypesInformation) + ALIGN_UP( sizeof(*TypesInformation), ULONG_PTR ));

            for (i=0; i<OBP_MAX_DEFINED_OBJECT_TYPES; i++) {

                ObjectType = ObpObjectTypes[ i ];

                if (ObjectType == NULL) {

                    break;
                }

                Status = ObQueryTypeInfo( ObjectType,
                                          TypeInfo,
                                          ObjectInformationLength,
                                          &TempReturnLength );

                if (NT_SUCCESS( Status )) {

                    TypeInfo = (POBJECT_TYPE_INFORMATION)
                        ((PCHAR)(TypeInfo+1) + ALIGN_UP( TypeInfo->TypeName.MaximumLength, ULONG_PTR ));
                }
            }

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            Status = GetExceptionCode();
        }

        break;

    case ObjectHandleFlagInformation:

        try {

            //
            //  Set the amount of data we are going to return
            //

            TempReturnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION);

            HandleFlags = (POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation;

            //
            //  Make sure we have enough room for the query, and if so we'll
            //  set the output based on the flags stored in the handle
            //

            if (ObjectInformationLength < sizeof( OBJECT_HANDLE_FLAG_INFORMATION)) {

                Status = STATUS_INFO_LENGTH_MISMATCH;

            } else {

                HandleFlags->Inherit = FALSE;

                if (HandleInformation.HandleAttributes & OBJ_INHERIT) {

                    HandleFlags->Inherit = TRUE;
                }

                HandleFlags->ProtectFromClose = FALSE;

                if (HandleInformation.HandleAttributes & OBJ_PROTECT_CLOSE) {

                    HandleFlags->ProtectFromClose = TRUE;
                }
            }

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            Status = GetExceptionCode();
        }

        break;

    default:

        //
        //  To get to this point we must have had an object and the
        //  information class is not defined, so we should dereference the
        //  object and return to our user the bad status
        //

        ObDereferenceObject( Object );

        return( STATUS_INVALID_INFO_CLASS );
    }

    //
    //  Now if the caller asked for a return length we'll set it from
    //  our local copy
    //

    try {

        if (ARGUMENT_PRESENT( ReturnLength ) ) {

            *ReturnLength = TempReturnLength;
        }

    } except( EXCEPTION_EXECUTE_HANDLER ) {

        //
        //  Fall through, since we cannot undo what we have done.
        //
    }

    //
    //  In the end we can free the object if there was one and return
    //  to our caller
    //

    if (Object != NULL) {

        ObDereferenceObject( Object );
    }

    return( Status );
}

NTSTATUS
ObSetHandleAttributes (
    __in HANDLE Handle,
    __in POBJECT_HANDLE_FLAG_INFORMATION HandleFlags,
    __in KPROCESSOR_MODE PreviousMode
    )
{
    BOOLEAN AttachedToProcess = FALSE;
    KAPC_STATE ApcState;
    OBP_SET_HANDLE_ATTRIBUTES CapturedInformation;
    PVOID ObjectTable;
    HANDLE ObjectHandle;
    NTSTATUS Status;

    PAGED_CODE();

    CapturedInformation.PreviousMode = PreviousMode;
    CapturedInformation.ObjectInformation = *HandleFlags;

    //
    //  Get the address of the object table for the current process.  Or
    //  get the system handle table if this is a kernel handle and we are
    //  in kernel mode
    //

    if (IsKernelHandle( Handle, PreviousMode )) {

        //
        //  Make the handle look like a regular handle
        //

        ObjectHandle = DecodeKernelHandle( Handle );

        //
        //  The global kernel handle table
        //

        ObjectTable = ObpKernelHandleTable;

        //
        //  Go to the system process
        //

        if (PsGetCurrentProcess() != PsInitialSystemProcess) {
            KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
            AttachedToProcess = TRUE;
        }

    } else {

        ObjectTable = ObpGetObjectTable();
        ObjectHandle = Handle;
    }

    //
    //  Make the change to the handle table entry.  The callback
    //  routine will do the actual change
    //

    if (ExChangeHandle( ObjectTable,
                        ObjectHandle,
                        ObpSetHandleAttributes,
                        (ULONG_PTR)&CapturedInformation) ) {

        Status = STATUS_SUCCESS;

    } else {

        Status = STATUS_ACCESS_DENIED;
    }

    //
    //  If we are attached to the system process then return
    //  back to our caller
    //

    if (AttachedToProcess) {
        KeUnstackDetachProcess(&ApcState);
        AttachedToProcess = FALSE;
    }
    return Status;
}

NTSTATUS
NTAPI
NtSetInformationObject (
    __in HANDLE Handle,
    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
    __in_bcount(ObjectInformationLength) PVOID ObjectInformation,
    __in ULONG ObjectInformationLength
    )

/*++

Routine description:

    This routine is used to set handle information about a specified
    handle

Arguments:

    Handle - Supplies the handle being modified

    ObjectInformationClass - Specifies the class of information being
        modified.  The only accepted value is ObjectHandleFlagInformation

    ObjectInformation - Supplies the buffer containing the handle
        flag information structure

    ObjectInformationLength - Specifies the length, in bytes, of the
        object information buffer

Return Value:

    An appropriate status value

--*/

{
    NTSTATUS Status;
    OBJECT_HANDLE_FLAG_INFORMATION CapturedFlags;
    KPROCESSOR_MODE PreviousMode;

    PAGED_CODE();


    Status = STATUS_INVALID_INFO_CLASS;

    switch (ObjectInformationClass) {
         
        case ObjectHandleFlagInformation:
            {
                if (ObjectInformationLength != sizeof(OBJECT_HANDLE_FLAG_INFORMATION)) {

                    return STATUS_INFO_LENGTH_MISMATCH;
                }

                //
                //  Get previous processor mode and probe and capture the input
                //  buffer
                //

                PreviousMode = KeGetPreviousMode();

                try {

                    if (PreviousMode != KernelMode) {

                        ProbeForRead(ObjectInformation, ObjectInformationLength, 1);
                    }

                    CapturedFlags = *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation;

                } except(ExSystemExceptionFilter()) {

                    return GetExceptionCode();
                }

                Status = ObSetHandleAttributes (Handle,
                                                &CapturedFlags,
                                                PreviousMode);

            }

            break;
        
        case ObjectSessionInformation:
            {
                PreviousMode = KeGetPreviousMode();

                if (!SeSinglePrivilegeCheck( SeTcbPrivilege,
                                             PreviousMode)) {

                    Status = STATUS_PRIVILEGE_NOT_HELD;

                } else {
                    
                    PVOID Object;
                    OBJECT_HANDLE_INFORMATION HandleInformation;

                    Status = ObReferenceObjectByHandle(Handle, 
                                                       0, 
                                                       ObpDirectoryObjectType,
                                                       PreviousMode,
                                                       &Object,
                                                       &HandleInformation
                                                       );

                    if (NT_SUCCESS(Status)) {

                        POBJECT_DIRECTORY Directory;
                        OBP_LOOKUP_CONTEXT LockContext;
                        Directory = (POBJECT_DIRECTORY)Object;

                        ObpInitializeLookupContext( &LockContext );
                        
                        ObpLockDirectoryExclusive(Directory, &LockContext);

                        Directory->SessionId = PsGetCurrentProcessSessionId();

                        ObpUnlockDirectory(Directory, &LockContext);

                        ObDereferenceObject(Object);
                    }
                }
            }

            break;
    }

    //
    //  And return to our caller
    //

    return Status;
}


#define OBP_MISSING_NAME_LITERAL L"..."
#define OBP_MISSING_NAME_LITERAL_SIZE (sizeof( OBP_MISSING_NAME_LITERAL ) - sizeof( UNICODE_NULL ))

NTSTATUS
ObQueryNameString (
    __in PVOID Object,
    __out_bcount(Length) POBJECT_NAME_INFORMATION ObjectNameInfo,
    __in ULONG Length,
    __out PULONG ReturnLength
    )
/*++

Routine description:

    This routine processes a query of an object's name information

Arguments:

    Object - Supplies the object being queried

    ObjectNameInfo - Supplies the buffer to store the name string
        information

    Length - Specifies the length, in bytes, of the original object
        name info buffer.

    ReturnLength - Contains the number of bytes already used up
        in the object name info. On return this receives an updated
        byte count.

        (Length minus ReturnLength) is really now many bytes are left
        in the output buffer.  The buffer supplied to this call may
        actually be offset within the original users buffer

Return Value:

    An appropriate status value

--*/

{
    return ObpQueryNameString( Object,
                               ObjectNameInfo,
                               Length,
                               ReturnLength,
                               KernelMode );
}

NTSTATUS
ObpQueryNameString (
    IN PVOID Object,
    OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
    IN ULONG Length,
    OUT PULONG ReturnLength,
    IN KPROCESSOR_MODE Mode
    )

/*++

Routine description:

    This routine processes a query of an object's name information

Arguments:

    Object - Supplies the object being queried

    ObjectNameInfo - Supplies the buffer to store the name string
        information

    Length - Specifies the length, in bytes, of the original object
        name info buffer.

    ReturnLength - Contains the number of bytes already used up
        in the object name info. On return this receives an updated
        byte count.

        (Length minus ReturnLength) is really now many bytes are left
        in the output buffer.  The buffer supplied to this call may
        actually be offset within the original users buffer

    Mode - Mode of caller

Return Value:

    An appropriate status value

--*/

{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    POBJECT_HEADER ObjectHeader;
    POBJECT_HEADER_NAME_INFO NameInfo;
    POBJECT_HEADER ObjectDirectoryHeader;
    POBJECT_DIRECTORY ObjectDirectory;
    ULONG NameInfoSize = 0;
    PUNICODE_STRING String;
    PWCH StringBuffer;
    ULONG NameSize;
    PVOID ReferencedObject = NULL;
    BOOLEAN DoFullQuery = TRUE;
    ULONG BufferLength;
    PWCH OriginalBuffer;
    BOOLEAN ForceRetry = FALSE;

    PAGED_CODE();

    //
    //  Get the object header and name info record if it exists
    //

    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    NameInfo = ObpReferenceNameInfo( ObjectHeader );

    //
    //  If the object type has a query name callback routine then
    //  that is how we get the name
    //

    if (ObjectHeader->Type->TypeInfo.QueryNameProcedure != NULL) {

        try {

#if DBG
            KIRQL SaveIrql;
#endif

            ObpBeginTypeSpecificCallOut( SaveIrql );
            ObpEndTypeSpecificCallOut( SaveIrql, "Query", ObjectHeader->Type, Object );

            Status = (*ObjectHeader->Type->TypeInfo.QueryNameProcedure)( Object,
                                                                         (BOOLEAN)((NameInfo != NULL) && (NameInfo->Name.Length != 0)),
                                                                         ObjectNameInfo,
                                                                         Length,
                                                                         ReturnLength,
                                                                         Mode );

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            Status = GetExceptionCode();
        }

        ObpDereferenceNameInfo( NameInfo );

        return( Status );
    }

    //
    //  Otherwise, the object type does not specify a query name
    //  procedure so we get to do the work.  The first thing
    //  to check is if the object doesn't even have a name.  If
    //  object doesn't have a name then we'll return an empty name
    //  info structure.
    //

RETRY:
    if ((NameInfo == NULL) || (NameInfo->Name.Buffer == NULL)) {

        //
        //  Compute the length of our return buffer, set the output
        //  if necessary and make sure the supplied buffer is large
        //  enough
        //

        NameInfoSize = sizeof( OBJECT_NAME_INFORMATION );

        try {

            *ReturnLength = NameInfoSize;

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            ObpDereferenceNameInfo( NameInfo );

            return( GetExceptionCode() );
        }

        if (Length < NameInfoSize) {

            ObpDereferenceNameInfo( NameInfo );

            return( STATUS_INFO_LENGTH_MISMATCH );
        }

        //
        //  Initialize the output buffer to be an empty string
        //  and then return to our caller
        //

        try {

            ObjectNameInfo->Name.Length = 0;
            ObjectNameInfo->Name.MaximumLength = 0;
            ObjectNameInfo->Name.Buffer = NULL;

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            //
            //  Fall through, since we cannot undo what we have done.
            //
            ObpDereferenceNameInfo(NameInfo);

            return( GetExceptionCode() );
        }

        ObpDereferenceNameInfo(NameInfo);

        return( STATUS_SUCCESS );
    }

    try {

        //
        //  The object does have a name but now see if this is
        //  just the root directory object in which case the name size
        //  is only the "\" character
        //

        if (Object == ObpRootDirectoryObject) {

            NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR );

        } else {

            //
            //  The named object is not the root so for every directory
            //  working out way up we'll add its size to the name keeping
            //  track of "\" characters inbetween each component.  We first
            //  start with the object name itself and then move on to
            //  the directories
            //

            ObjectDirectory = NameInfo->Directory;
            
            if (ObjectDirectory) {
                
                ObfReferenceObject( ObjectDirectory );
                ReferencedObject = ObjectDirectory;
            }
            
            NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;

            ObpDereferenceNameInfo( NameInfo );
            NameInfo = NULL;

            //
            //  While we are not at the root we'll keep moving up
            //

            while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) {

                //
                //  Get the name information for this directory
                //


                ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory );
                NameInfo = ObpReferenceNameInfo( ObjectDirectoryHeader );

                if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {

                    //
                    //  This directory has a name so add it to the accumulated
                    //  size and move up the tree
                    //

                    NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;
                    
                    ObjectDirectory = NameInfo->Directory;

                    if (ObjectDirectory) {

                        ObfReferenceObject( ObjectDirectory );
                    }
                    
                    ObpDereferenceNameInfo( NameInfo );
                    NameInfo = NULL;
                    ObDereferenceObject( ReferencedObject );
                    
                    ReferencedObject = ObjectDirectory;

                    //
                    //  UNICODE_STRINGs can only hold MAXUSHORT bytes.
                    //

                    if (NameSize > MAXUSHORT) {

                        break;
                    }

                } else {

                    //
                    //  This directory does not have a name so we'll give it
                    //  the "..." name and stop the loop
                    //

                    NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + OBP_MISSING_NAME_LITERAL_SIZE;
                    break;
                }
            }
        }

        //
        //  UNICODE_STRINGs can only hold MAXUSHORT bytes
        //

        if (NameSize > MAXUSHORT) {

            Status = STATUS_NAME_TOO_LONG;
            DoFullQuery = FALSE;
            leave;
        }

        //
        //  At this point NameSize is the number of bytes we need to store the
        //  name of the object from the root down.  The total buffer size we are
        //  going to need will include this size, plus object name information
        //  structure, plus an ending null character
        //

        NameInfoSize = NameSize + sizeof( OBJECT_NAME_INFORMATION ) + sizeof( UNICODE_NULL );

        //
        //  Set the output size and make sure the supplied buffer is large enough
        //  to hold the information
        //

        try {

            *ReturnLength = NameInfoSize;

        } except( EXCEPTION_EXECUTE_HANDLER ) {

            Status = GetExceptionCode();
            DoFullQuery = FALSE;
            leave;
        }

        if (Length < NameInfoSize) {

            Status = STATUS_INFO_LENGTH_MISMATCH;
            DoFullQuery = FALSE;
            leave;
        }

    } finally {

        ObpDereferenceNameInfo( NameInfo );
        NameInfo = NULL;

        if (ReferencedObject) {

            ObDereferenceObject( ReferencedObject );
            ReferencedObject = NULL;
        }
    }
    
    if (!DoFullQuery) {

        return Status;
    }

    NameInfo = ObpReferenceNameInfo( ObjectHeader );

    //
    //  Check whether someone else removed the name meanwhile
    //

    if (!NameInfo) {

        //
        //  The name is gone, we need to jump to the code path that handles
        //  empty object name
        //

        goto RETRY;
    }

    //
    //  Set the String buffer to point to the byte right after the
    //  last byte in the output string.  This following logic actually
    //  fills in the buffer backwards working from the name back to the
    //  root
    //

    StringBuffer = (PWCH)ObjectNameInfo;
    StringBuffer = (PWCH)((PCH)StringBuffer + NameInfoSize);
    OriginalBuffer = (PWCH)((PCH)ObjectNameInfo + sizeof( OBJECT_NAME_INFORMATION ));

    try {

        //
        //  Terminate the string with a null and backup one unicode
        //  character
        //

        *--StringBuffer = UNICODE_NULL;

        //
        //  If the object in question is not the root directory
        //  then we are going to put its name in the string buffer
        //  When we finally reach the root directory we'll append on
        //  the final "\"
        //

        if (Object != ObpRootDirectoryObject) {

            //
            //  Add in the objects name
            //

            String = &NameInfo->Name;
            StringBuffer = (PWCH)((PCH)StringBuffer - String->Length);

            if (StringBuffer <= OriginalBuffer) {

                ForceRetry = TRUE;
                leave;
            }

            RtlCopyMemory( StringBuffer, String->Buffer, String->Length );

            //
            //  While we are not at the root directory we'll keep
            //  moving up
            //

            ObjectDirectory = NameInfo->Directory;

            if (ObjectDirectory) {

                //
                //  Reference the directory for this object to make sure it's
                //  valid while looking up
                //

                ObfReferenceObject( ObjectDirectory );
                ReferencedObject = ObjectDirectory;
            }
                
            ObpDereferenceNameInfo( NameInfo );
            NameInfo = NULL;

            while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) {

                //
                //  Get the name information for this directory
                //

                ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory );
                NameInfo = ObpReferenceNameInfo( ObjectDirectoryHeader );

                //
                //  Tack on the "\" between the last name we added and
                //  this new name
                //

                *--StringBuffer = OBJ_NAME_PATH_SEPARATOR;

                //
                //  Preappend the directory name, if it has one, and
                //  move up to the next directory.
                //

                if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {

                    String = &NameInfo->Name;
                    StringBuffer = (PWCH)((PCH)StringBuffer - String->Length);
                    
                    if (StringBuffer <= OriginalBuffer) {
                        
                        ForceRetry = TRUE;
                        leave;
                    }

                    RtlCopyMemory( StringBuffer, String->Buffer, String->Length );

                    ObjectDirectory = NameInfo->Directory;

                    if (ObjectDirectory) {

                        ObfReferenceObject( ObjectDirectory );
                    }

                    //
                    //  Dereference the name info (it must be done before dereferencing the object)
                    //

                    ObpDereferenceNameInfo( NameInfo );
                    NameInfo = NULL;

                    ObDereferenceObject( ReferencedObject );

                    ReferencedObject = ObjectDirectory;

                } else {

                    //
                    //  The directory is nameless so use the "..." for
                    //  its name and break out of the loop
                    //

                    StringBuffer = (PWCH)((PCH)StringBuffer - OBP_MISSING_NAME_LITERAL_SIZE);

                    //
                    //  Because we don't hold the global lock any more, we can have a special case
                    //  where a directory of 1 or 2 letters name AND inserted into the root
                    //  can go away meanwhile and "..." will be too long to fit the remaining space
                    //  We already copied the buffer so we cannot rollback everything we done.
                    //  We'll return \..  if the original directory was 1 char length,
                    //  \..\ for 2 char length
                    //

                    if (StringBuffer < OriginalBuffer) {

                        StringBuffer = OriginalBuffer;
                    }

                    RtlCopyMemory( StringBuffer,
                                   OBP_MISSING_NAME_LITERAL,
                                   OBP_MISSING_NAME_LITERAL_SIZE );

                    //
                    //  Test if we are in the case commented above. If yes, we need to move the 
                    //  current pointer to the next char, so the final assignment for \ a few lines
                    //  below will take effect on the start of the block.
                    //

                    if (StringBuffer == OriginalBuffer) {

                        StringBuffer++;
                    }

                    break;
                }
            }
        }

        //
        //  Tack on the "\" for the root directory and then set the
        //  output unicode string variable to have the right size
        //  and point to the right spot.
        //

        *--StringBuffer = OBJ_NAME_PATH_SEPARATOR;

        BufferLength = (USHORT)((ULONG_PTR)ObjectNameInfo + NameInfoSize - (ULONG_PTR)StringBuffer);

        ObjectNameInfo->Name.MaximumLength = (USHORT)BufferLength;
        ObjectNameInfo->Name.Length = (USHORT)(BufferLength - sizeof( UNICODE_NULL ));
        ObjectNameInfo->Name.Buffer = OriginalBuffer;

        //
        //  If one of the parent directories disappeared, the final length
        //  will be smaller than we estimated before. We need to move the string to
        //  the beginning and adjust the returned size.
        //

        if (OriginalBuffer != StringBuffer) {

            RtlMoveMemory(OriginalBuffer, StringBuffer, BufferLength);
            
            *ReturnLength = BufferLength + sizeof( OBJECT_NAME_INFORMATION );
        }

    } except( EXCEPTION_EXECUTE_HANDLER ) {

        //
        //  Fall through, since we cannot undo what we have done.
        //
        //  This should probably get the exception code and return
        //  that value. However, the caller we'll get an exception
        //  at the first access of the ObjectNameInfo
        //
    }

    ObpDereferenceNameInfo( NameInfo );
    
    if (ReferencedObject) {

        ObDereferenceObject( ReferencedObject );
    }

    if (ForceRetry) {

        //
        //  The query failed maybe because the object name changed during the query
        //
        
        NameInfo = ObpReferenceNameInfo( ObjectHeader );
        ForceRetry = FALSE;

        goto RETRY;
    }

    return STATUS_SUCCESS;
}



NTSTATUS
ObQueryTypeName (
    IN PVOID Object,
    PUNICODE_STRING ObjectTypeName,
    IN ULONG Length,
    OUT PULONG ReturnLength
    )

/*++

Routine description:

    This routine processes a query of an object's type name

Arguments:

    Object - Supplies the object being queried

    ObjectTypeName - Supplies the buffer to store the type name
        string information

    Length - Specifies the length, in bytes, of the object type
        name buffer

    ReturnLength - Contains the number of bytes already used up
        in the object type name buffer. On return this receives
        an updated byte count

        (Length minus ReturnLength) is really now many bytes are left
        in the output buffer.  The buffer supplied to this call may
        actually be offset within the original users buffer

Return Value:

    An appropriate status value

--*/

{
    POBJECT_TYPE ObjectType;
    POBJECT_HEADER ObjectHeader;
    ULONG TypeNameSize;
    PUNICODE_STRING String;
    PWCH StringBuffer;
    ULONG NameSize;

    PAGED_CODE();

    //
    //  From the object get its object type and from that get the size of
    //  the object type name.  The total size for we need for the output
    //  buffer must fit the name, a terminating null, and a preceding
    //  unicode string structure
    //

    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    ObjectType = ObjectHeader->Type;

    NameSize = ObjectType->Name.Length;
    TypeNameSize = NameSize + sizeof( UNICODE_NULL ) + sizeof( UNICODE_STRING );

    //
    //  Update the number of bytes we need and make sure the output buffer is
    //  large enough
    //

    try {

        *ReturnLength = TypeNameSize;

    } except( EXCEPTION_EXECUTE_HANDLER ) {

        return( GetExceptionCode() );
    }

    if (Length < TypeNameSize) {

        return( STATUS_INFO_LENGTH_MISMATCH );
    }

    //
    //  Set string buffer to point to the one byte beyond the
    //  buffer that we're going to fill in
    //

    StringBuffer = (PWCH)ObjectTypeName;
    StringBuffer = (PWCH)((PCH)StringBuffer + TypeNameSize);

    String = &ObjectType->Name;

    try {

        //
        //  Tack on the terminating null character and copy over
        //  the type name
        //

        *--StringBuffer = UNICODE_NULL;

        StringBuffer = (PWCH)((PCH)StringBuffer - String->Length);

        RtlCopyMemory( StringBuffer, String->Buffer, String->Length );

        //
        //  Now set the preceding unicode string to have the right
        //  lengths and to point to this buffer
        //

        ObjectTypeName->Length = (USHORT)NameSize;
        ObjectTypeName->MaximumLength = (USHORT)(NameSize+sizeof( UNICODE_NULL ));
        ObjectTypeName->Buffer = StringBuffer;

    } except( EXCEPTION_EXECUTE_HANDLER ) {

        //
        // Fall through, since we cannot undo what we have done.
        //
    }

    return( STATUS_SUCCESS );
}


NTSTATUS
ObQueryTypeInfo (
    IN POBJECT_TYPE ObjectType,
    OUT POBJECT_TYPE_INFORMATION ObjectTypeInfo,
    IN ULONG Length,
    OUT PULONG ReturnLength
    )

/*++

Routine description:

    This routine processes the query for object type information

Arguments:

    Object - Supplies a pointer to the object type being queried

    ObjectTypeInfo - Supplies the buffer to store the type information

    Length - Specifies the length, in bytes, of the object type
        information buffer

    ReturnLength - Contains the number of bytes already used up
        in the object type information buffer. On return this receives
        an updated byte count

        (Length minus ReturnLength) is really now many bytes are left
        in the output buffer.  The buffer supplied to this call may
        actually be offset within the original users buffer

Return Value:

    An appropriate status value

--*/

{
    NTSTATUS Status;

    try {

        //
        //  The total number of bytes needed for this query includes the
        //  object type information structure plus the name of the type
        //  rounded up to a ulong boundary
        //

        *ReturnLength += sizeof( *ObjectTypeInfo ) + ALIGN_UP( ObjectType->Name.MaximumLength, ULONG );

        //
        //  Make sure the buffer is large enough for this information and
        //  then fill in the record
        //

        if (Length < *ReturnLength) {

            Status = STATUS_INFO_LENGTH_MISMATCH;

        } else {

            ObjectTypeInfo->TotalNumberOfObjects = ObjectType->TotalNumberOfObjects;
            ObjectTypeInfo->TotalNumberOfHandles = ObjectType->TotalNumberOfHandles;
            ObjectTypeInfo->HighWaterNumberOfObjects = ObjectType->HighWaterNumberOfObjects;
            ObjectTypeInfo->HighWaterNumberOfHandles = ObjectType->HighWaterNumberOfHandles;
            ObjectTypeInfo->InvalidAttributes = ObjectType->TypeInfo.InvalidAttributes;
            ObjectTypeInfo->GenericMapping = ObjectType->TypeInfo.GenericMapping;
            ObjectTypeInfo->ValidAccessMask = ObjectType->TypeInfo.ValidAccessMask;
            ObjectTypeInfo->SecurityRequired = ObjectType->TypeInfo.SecurityRequired;
            ObjectTypeInfo->MaintainHandleCount = ObjectType->TypeInfo.MaintainHandleCount;
            ObjectTypeInfo->PoolType = ObjectType->TypeInfo.PoolType;
            ObjectTypeInfo->DefaultPagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;
            ObjectTypeInfo->DefaultNonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;

            //
            //  The type name goes right after this structure.  We cannot use
            //  rtl routine like RtlCopyUnicodeString that might use the local
            //  memory to keep state, because this is the user buffer and it
            //  could be changing by user
            //

            ObjectTypeInfo->TypeName.Buffer = (PWSTR)(ObjectTypeInfo+1);
            ObjectTypeInfo->TypeName.Length = ObjectType->Name.Length;
            ObjectTypeInfo->TypeName.MaximumLength = ObjectType->Name.MaximumLength;

            RtlCopyMemory( (PWSTR)(ObjectTypeInfo+1),
                           ObjectType->Name.Buffer,
                           ObjectType->Name.Length );

            ((PWSTR)(ObjectTypeInfo+1))[ ObjectType->Name.Length/sizeof(WCHAR) ] = UNICODE_NULL;

            Status = STATUS_SUCCESS;
        }

    } except( EXCEPTION_EXECUTE_HANDLER ) {

        Status = GetExceptionCode();
    }

    return Status;
}


NTSTATUS
ObQueryObjectAuditingByHandle (
    __in HANDLE Handle,
    __out PBOOLEAN GenerateOnClose
    )

/*++

Routine description:

    This routine tells the caller if the indicated handle will
    generate an audit if it is closed

Arguments:

    Handle - Supplies the handle being queried

    GenerateOnClose - Receives TRUE if the handle will generate
        an audit if closed and FALSE otherwise

Return Value:

    An appropriate status value

--*/

{
    PHANDLE_TABLE ObjectTable;
    PHANDLE_TABLE_ENTRY ObjectTableEntry;
    ULONG CapturedAttributes;
    NTSTATUS Status;
    PETHREAD CurrentThread;

    PAGED_CODE();

    ObpValidateIrql( "ObQueryObjectAuditingByHandle" );

    CurrentThread = PsGetCurrentThread ();

    //
    //  For the current process we'll grab its object table and
    //  then get the object table entry
    //

    if (IsKernelHandle( Handle, KeGetPreviousMode() ))  {

        Handle = DecodeKernelHandle( Handle );

        ObjectTable = ObpKernelHandleTable;

    } else {

        ObjectTable = PsGetCurrentProcessByThread (CurrentThread)->ObjectTable;
    }

    //
    //  Protect ourselves from being interrupted while we hold a handle table
    //  entry lock
    //

    KeEnterCriticalRegionThread(&CurrentThread->Tcb);

    ObjectTableEntry = ExMapHandleToPointer( ObjectTable,
                                             Handle );

    //
    //  If we were given a valid handle we'll look at the attributes
    //  stored in the object table entry to decide if we generate
    //  an audit on close
    //

    if (ObjectTableEntry != NULL) {

        CapturedAttributes = ObjectTableEntry->ObAttributes;

        ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry );

        if (CapturedAttributes & OBJ_AUDIT_OBJECT_CLOSE) {

            *GenerateOnClose = TRUE;

        } else {

            *GenerateOnClose = FALSE;
        }

        Status = STATUS_SUCCESS;

    } else {

        Status = STATUS_INVALID_HANDLE;
    }

    KeLeaveCriticalRegionThread(&CurrentThread->Tcb);

    return Status;
}


#if DBG
PUNICODE_STRING
ObGetObjectName (
    IN PVOID Object
    )

/*++

Routine description:

    This routine returns a pointer to the name of object

Arguments:

    Object - Supplies the object being queried

Return Value:

    The address of the unicode string that stores the object
    name if available and NULL otherwise

--*/

{
    POBJECT_HEADER ObjectHeader;
    POBJECT_HEADER_NAME_INFO NameInfo;

    //
    //  Translate the input object to a name info structure
    //

    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );

    //
    //  If the object has a name then return the address of
    //  the name otherwise return null
    //

    if ((NameInfo != NULL) && (NameInfo->Name.Length != 0)) {

        return &NameInfo->Name;

    } else {

        return NULL;
    }
}
#endif // DBG


//
//  Local support routine
//

BOOLEAN
ObpSetHandleAttributes (
    IN OUT PHANDLE_TABLE_ENTRY ObjectTableEntry,
    IN ULONG_PTR Parameter
    )

/*++

Routine description:

    This is the call back routine for the ExChangeHandle from
    NtSetInformationObject

Arguments:

    ObjectTableEntry - Supplies a pointer to the object table entry being
        modified

    Parameter - Supplies a pointer to the OBJECT_HANDLE_FLAG_INFORMATION
        structure to set into the table entry

Return Value:

    Returns TRUE if the operation is successful otherwise FALSE

--*/

{
    POBP_SET_HANDLE_ATTRIBUTES ObjectInformation;
    POBJECT_HEADER ObjectHeader;

    ObjectInformation = (POBP_SET_HANDLE_ATTRIBUTES)Parameter;

    //
    //  Get a pointer to the object type via the object header and if the
    //  caller has asked for inherit but the object type says that inherit
    //  is an invalid flag then return false
    //

    ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);

    if ((ObjectInformation->ObjectInformation.Inherit) &&
        ((ObjectHeader->Type->TypeInfo.InvalidAttributes & OBJ_INHERIT) != 0)) {

        return FALSE;
    }

    //
    //  For each piece of information (inherit and protect from close) that
    //  is in the object information buffer we'll set or clear the bits in
    //  the object table entry.  The bits modified are the low order bits of
    //  used to store the pointer to the object header.
    //

    if (ObjectInformation->ObjectInformation.Inherit) {

        ObjectTableEntry->ObAttributes |= OBJ_INHERIT;

    } else {

        ObjectTableEntry->ObAttributes &= ~OBJ_INHERIT;
    }

    if (ObjectInformation->ObjectInformation.ProtectFromClose) {
        
        ObjectTableEntry->GrantedAccess |= ObpAccessProtectCloseBit;

    } else {

        ObjectTableEntry->GrantedAccess &= ~ObpAccessProtectCloseBit;
    }

    //
    //  And return to our caller
    //

    return TRUE;
}

