/*++

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:

    obcreate.c

Abstract:

    Object creation

--*/

#include "obp.h"

#undef ObCreateObject

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ObCreateObject)
#pragma alloc_text(PAGE, ObpCaptureObjectCreateInformation)
#pragma alloc_text(PAGE, ObpCaptureObjectName)
#pragma alloc_text(PAGE, ObpAllocateObjectNameBuffer)
#pragma alloc_text(PAGE, ObpFreeObjectNameBuffer)
#pragma alloc_text(PAGE, ObDeleteCapturedInsertInfo)
#pragma alloc_text(PAGE, ObpAllocateObject)
#pragma alloc_text(PAGE, ObpFreeObject)
#pragma alloc_text(PAGE, ObFreeObjectCreateInfoBuffer)
#endif


#if DBG

BOOLEAN ObWatchHandles = FALSE;

//
//  The following variable is only used on a checked build to control
//  echoing out the allocs and frees of objects
//

BOOLEAN ObpShowAllocAndFree;
#else

const BOOLEAN ObWatchHandles = FALSE;

#endif

//
//  Local performance counters
//

#if DBG
ULONG ObpObjectsCreated;
ULONG ObpObjectsWithPoolQuota;
ULONG ObpObjectsWithHandleDB;
ULONG ObpObjectsWithName;
ULONG ObpObjectsWithCreatorInfo;
#endif // DBG

C_ASSERT ( (FIELD_OFFSET (OBJECT_HEADER, Body) % MEMORY_ALLOCATION_ALIGNMENT) == 0 );
C_ASSERT ( (sizeof (OBJECT_HEADER_CREATOR_INFO) % MEMORY_ALLOCATION_ALIGNMENT) == 0 );
C_ASSERT ( (sizeof (OBJECT_HEADER_NAME_INFO) % MEMORY_ALLOCATION_ALIGNMENT) == 0 );
C_ASSERT ( (sizeof (OBJECT_HEADER_QUOTA_INFO) % MEMORY_ALLOCATION_ALIGNMENT) == 0 );


NTSTATUS
ObCreateObject (
    __in KPROCESSOR_MODE ProbeMode,
    __in POBJECT_TYPE ObjectType,
    __in POBJECT_ATTRIBUTES ObjectAttributes,
    __in KPROCESSOR_MODE OwnershipMode,
    __inout_opt PVOID ParseContext,
    __in ULONG ObjectBodySize,
    __in ULONG PagedPoolCharge,
    __in ULONG NonPagedPoolCharge,
    __out PVOID *Object
    )

/*++

Routine Description:

    This functions allocates space for an NT Object from either
    Paged or NonPaged pool. It captures the optional name and
    SECURITY_DESCRIPTOR parameters for later use when the object is
    inserted into an object table.  No quota is charged at this time.
    That occurs when the object is inserted into an object table.

Arguments:

    ProbeMode - The processor mode to consider when doing a probe
        of the input parameters

    ObjectType - A pointer of the type returned by ObCreateObjectType
        that gives the type of object being created.

    ObjectAttributes - Optionally supplies the attributes of the object
        being created (such as its name)

    OwnershipMode - The processor mode of who is going to own the object

    ParseContext - Ignored

    ObjectBodySize - Number of bytes to allocate for the object body.  The
        object body immediately follows the object header in memory and are
        part of a single allocation.

    PagedPoolCharge - Supplies the amount of paged pool to charge for the
        object.  If zero is specified then the default charge for the object
        type is used.

    NonPagedPoolCharge - Supplies the amount of nonpaged pool to charge for
        the object.  If zero is specified then the default charge for the
        object type is used.

    Object - Receives a pointer to the newly created object

Return Value:

    Following errors can occur:

        - invalid object type
        - insufficient memory

--*/

{
    UNICODE_STRING CapturedObjectName;
    POBJECT_CREATE_INFORMATION ObjectCreateInfo;
    POBJECT_HEADER ObjectHeader;
    NTSTATUS Status;

    PAGED_CODE();

    UNREFERENCED_PARAMETER (ParseContext);

    //
    //  Allocate a buffer to capture the object creation information.
    //

    ObjectCreateInfo = ObpAllocateObjectCreateInfoBuffer();

    if (ObjectCreateInfo == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;

    } else {

        //
        //  Capture the object attributes, quality of service, and object
        //  name, if specified. Otherwise, initialize the captured object
        //  name, the security quality of service, and the create attributes
        //  to default values.
        //

        Status = ObpCaptureObjectCreateInformation( ObjectType,
                                                    ProbeMode,
                                                    OwnershipMode,
                                                    ObjectAttributes,
                                                    &CapturedObjectName,
                                                    ObjectCreateInfo,
                                                    FALSE );

        if (NT_SUCCESS(Status)) {

            //
            //  If the creation attributes are invalid, then return an error
            //  status.
            //

            if (ObjectType->TypeInfo.InvalidAttributes & ObjectCreateInfo->Attributes) {

                Status = STATUS_INVALID_PARAMETER;

            } else {

                //
                //  Set the paged and nonpaged pool quota charges for the
                //  object allocation.
                //

                if (PagedPoolCharge == 0) {

                    PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;
                }

                if (NonPagedPoolCharge == 0) {

                    NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;
                }

                ObjectCreateInfo->PagedPoolCharge = PagedPoolCharge;
                ObjectCreateInfo->NonPagedPoolCharge = NonPagedPoolCharge;

                //
                //  Allocate and initialize the object.
                //

                Status = ObpAllocateObject( ObjectCreateInfo,
                                            OwnershipMode,
                                            ObjectType,
                                            &CapturedObjectName,
                                            ObjectBodySize,
                                            &ObjectHeader );

                if (NT_SUCCESS(Status)) {

                    //
                    //  If a permanent object is being created, then check if
                    //  the caller has the appropriate privilege.
                    //

                    *Object = &ObjectHeader->Body;

                    if (ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT) {

                        if (!SeSinglePrivilegeCheck( SeCreatePermanentPrivilege,
                                                     ProbeMode)) {

                            ObpFreeObject(*Object);

                            Status = STATUS_PRIVILEGE_NOT_HELD;
                        }
                    }

#ifdef POOL_TAGGING
                    if (ObpTraceEnabled && NT_SUCCESS(Status)) {

                        //
                        //  Register the object and push stack information for the
                        //  first reference
                        //

                        ObpRegisterObject( ObjectHeader );
                        ObpPushStackInfo( ObjectHeader, TRUE );
                    }
#endif //POOL_TAGGING

                    //
                    //  Here is the only successful path out of this module but
                    //  this path can also return privilege not held.  In the 
                    //  error case, all the resources have already been freed
                    //  by ObpFreeObject.
                    //

                    return Status;
                }
            }

            //
            //  An error path, free the create information.
            //

            ObpReleaseObjectCreateInformation(ObjectCreateInfo);

            if (CapturedObjectName.Buffer != NULL) {

                ObpFreeObjectNameBuffer(&CapturedObjectName);
            }
        }

        //
        //  An error path, free object creation information buffer.
        //

        ObpFreeObjectCreateInfoBuffer(ObjectCreateInfo);
    }

    //
    //  An error path
    //

    return Status;
}


NTSTATUS
ObpCaptureObjectCreateInformation (
    IN POBJECT_TYPE ObjectType OPTIONAL,
    IN KPROCESSOR_MODE ProbeMode,
    IN KPROCESSOR_MODE CreatorMode,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN OUT PUNICODE_STRING CapturedObjectName,
    IN POBJECT_CREATE_INFORMATION ObjectCreateInfo,
    IN LOGICAL UseLookaside
    )

/*++

Routine Description:

    This function captures the object creation information and stuff
    it into the input variable ObjectCreateInfo

Arguments:

    ObjectType - Specifies the type of object we expect to capture,
        currently ignored.

    ProbeMode - Specifies the processor mode for doing our parameter
        probes

    CreatorMode - Specifies the mode the object is being created for

    ObjectAttributes - Supplies the object attributes we are trying
        to capture

    CapturedObjectName - Receives the name of the object being created

    ObjectCreateInfo - Receives the create information for the object
        like its root, attributes, and security information

    UseLookaside - Specifies if we are to allocate the captured name
        buffer from the lookaside list or from straight pool.

Return Value:

    An appropriate status value

--*/

{
    PUNICODE_STRING ObjectName;
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    PSECURITY_QUALITY_OF_SERVICE SecurityQos;
    NTSTATUS Status;
    ULONG Size;

    PAGED_CODE();

    UNREFERENCED_PARAMETER (ObjectType);

    //
    //  Capture the object attributes, the security quality of service, if
    //  specified, and object name, if specified.
    //

    Status = STATUS_SUCCESS;

    RtlZeroMemory(ObjectCreateInfo, sizeof(OBJECT_CREATE_INFORMATION));

    try {

        if (ARGUMENT_PRESENT(ObjectAttributes)) {

            //
            //  Probe the object attributes if necessary.
            //

            if (ProbeMode != KernelMode) {

                ProbeForReadSmallStructure( ObjectAttributes,
                                            sizeof(OBJECT_ATTRIBUTES),
                                            sizeof(ULONG_PTR) );
            }

            if (ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES) ||
                (ObjectAttributes->Attributes & ~OBJ_ALL_VALID_ATTRIBUTES)) {

                Status = STATUS_INVALID_PARAMETER;

                goto failureExit;
            }

            //
            //  Capture the object attributes.
            //

            ObjectCreateInfo->RootDirectory = ObjectAttributes->RootDirectory;
            ObjectCreateInfo->Attributes = ObjectAttributes->Attributes & OBJ_ALL_VALID_ATTRIBUTES;
            //
            // Remove privileged option if passed in from user mode
            //
            if (CreatorMode != KernelMode) {
                ObjectCreateInfo->Attributes &= ~OBJ_KERNEL_HANDLE;
            } else if (ObWatchHandles) {
                if ((ObjectCreateInfo->Attributes&OBJ_KERNEL_HANDLE) == 0 &&
                    PsGetCurrentProcess() != PsInitialSystemProcess) {
                    DbgBreakPoint ();
                }
            }
            ObjectName = ObjectAttributes->ObjectName;
            SecurityDescriptor = ObjectAttributes->SecurityDescriptor;
            SecurityQos = ObjectAttributes->SecurityQualityOfService;

            if (ARGUMENT_PRESENT(SecurityDescriptor)) {

                Status = SeCaptureSecurityDescriptor( SecurityDescriptor,
                                                      ProbeMode,
                                                      PagedPool,
                                                      TRUE,
                                                      &ObjectCreateInfo->SecurityDescriptor );

                if (!NT_SUCCESS(Status)) {

                    KdPrint(( "OB: Failed to capture security descriptor at %08x - Status == %08x\n",
                              SecurityDescriptor,
                              Status) );

                    //
                    //  The cleanup routine depends on this being NULL if it isn't
                    //  allocated.  SeCaptureSecurityDescriptor may modify this
                    //  parameter even if it fails.
                    //

                    ObjectCreateInfo->SecurityDescriptor = NULL;

                    goto failureExit;
                }

                SeComputeQuotaInformationSize(  ObjectCreateInfo->SecurityDescriptor,
                                                &Size );

                ObjectCreateInfo->SecurityDescriptorCharge = SeComputeSecurityQuota( Size );
                ObjectCreateInfo->ProbeMode = ProbeMode;
            }

            if (ARGUMENT_PRESENT(SecurityQos)) {

                if (ProbeMode != KernelMode) {

                    ProbeForReadSmallStructure( SecurityQos, sizeof(*SecurityQos), sizeof(ULONG));
                }

                ObjectCreateInfo->SecurityQualityOfService = *SecurityQos;
                ObjectCreateInfo->SecurityQos = &ObjectCreateInfo->SecurityQualityOfService;
            }

        } else {

            ObjectName = NULL;
        }

    } except (ExSystemExceptionFilter()) {

        Status = GetExceptionCode();

        goto failureExit;
    }

    //
    //  If an object name is specified, then capture the object name.
    //  Otherwise, initialize the object name descriptor and check for
    //  an incorrectly specified root directory.
    //

    if (ARGUMENT_PRESENT(ObjectName)) {

        Status = ObpCaptureObjectName( ProbeMode,
                                       ObjectName,
                                       CapturedObjectName,
                                       UseLookaside );

    } else {

        CapturedObjectName->Buffer = NULL;
        CapturedObjectName->Length = 0;
        CapturedObjectName->MaximumLength = 0;

        if (ARGUMENT_PRESENT(ObjectCreateInfo->RootDirectory)) {

            Status = STATUS_OBJECT_NAME_INVALID;
        }
    }

    //
    //  If the completion status is not successful, and a security quality
    //  of service parameter was specified, then free the security quality
    //  of service memory.
    //

failureExit:

    if (!NT_SUCCESS(Status)) {

        ObpReleaseObjectCreateInformation(ObjectCreateInfo);
    }

    return Status;
}


NTSTATUS
ObpCaptureObjectName (
    IN KPROCESSOR_MODE ProbeMode,
    IN PUNICODE_STRING ObjectName,
    IN OUT PUNICODE_STRING CapturedObjectName,
    IN LOGICAL UseLookaside
    )

/*++

Routine Description:

    This function captures the object name but first verifies that
    it is at least properly sized.

Arguments:

    ProbeMode - Supplies the processor mode to use when probing
        the object name

    ObjectName - Supplies the caller's version of the object name

    CapturedObjectName - Receives the captured verified version
        of the object name

    UseLookaside - Indicates if the captured name buffer should be
        allocated from the lookaside list or from straight pool

Return Value:

    An appropriate status value

--*/

{
    PWCH FreeBuffer;
    UNICODE_STRING InputObjectName;
    ULONG Length;
    NTSTATUS Status;

    PAGED_CODE();

    //
    //  Initialize the object name descriptor and capture the specified name
    //  string.
    //

    CapturedObjectName->Buffer = NULL;
    CapturedObjectName->Length = 0;
    CapturedObjectName->MaximumLength = 0;

    Status = STATUS_SUCCESS;

    //
    //  Probe and capture the name string descriptor and probe the
    //  name string, if necessary.
    //

    FreeBuffer = NULL;

    try {

        if (ProbeMode != KernelMode) {

            ProbeAndReadUnicodeStringEx(&InputObjectName, ObjectName);

            ProbeForRead( InputObjectName.Buffer,
                          InputObjectName.Length,
                          sizeof(WCHAR) );

        } else {

            InputObjectName = *ObjectName;
        }

        //
        //  If the length of the string is not zero, then capture the string.
        //

        if (InputObjectName.Length != 0) {

            //
            //  If the length of the string is not an even multiple of the
            //  size of a UNICODE character or cannot be zero terminated,
            //  then return an error.
            //

            Length = InputObjectName.Length;

            if (((Length & (sizeof(WCHAR) - 1)) != 0) ||
                (Length == (MAXUSHORT - sizeof(WCHAR) + 1))) {

                Status = STATUS_OBJECT_NAME_INVALID;

            } else {

                //
                //  Allocate a buffer for the specified name string.
                //
                //  N.B. The name buffer allocation routine adds one
                //       UNICODE character to the length and initializes
                //       the string descriptor.
                //

                FreeBuffer = ObpAllocateObjectNameBuffer( Length,
                                                          UseLookaside,
                                                          CapturedObjectName );

                if (FreeBuffer == NULL) {

                    Status = STATUS_INSUFFICIENT_RESOURCES;

                } else {

                    //
                    //  Copy the specified name string to the destination
                    //  buffer.
                    //

                    RtlCopyMemory(FreeBuffer, InputObjectName.Buffer, Length);

                    //
                    //  Zero terminate the name string and initialize the
                    //  string descriptor.
                    //

                    FreeBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
                }
            }
        }

    } except(ExSystemExceptionFilter()) {

        Status = GetExceptionCode();

        if (FreeBuffer != NULL) {

            ExFreePool(FreeBuffer);
        }
    }

    return Status;
}


PWCHAR
ObpAllocateObjectNameBuffer (
    IN ULONG Length,
    IN LOGICAL UseLookaside,
    IN OUT PUNICODE_STRING ObjectName
    )

/*++

Routine Description:

    This function allocates an object name buffer.

    N.B. This function is nonpageable.

Arguments:

    Length - Supplies the length of the required buffer in bytes.

    UseLookaside - Supplies a logical variable that determines whether an
        attempt is made to allocate the name buffer from the lookaside list.

    ObjectName - Supplies a pointer to a name buffer string descriptor.

Return Value:

    If the allocation is successful, then name buffer string descriptor
    is initialized and the address of the name buffer is returned as the
    function value. Otherwise, a value of NULL is returned.

--*/

{
    PVOID Buffer;
    ULONG Maximum;

    //
    //  If allocation from the lookaside lists is specified and the buffer
    //  size is less than the size of lookaside list entries, then attempt
    //  to allocate the name buffer from the lookaside lists. Otherwise,
    //  attempt to allocate the name buffer from nonpaged pool.
    //

    Maximum = Length + sizeof(WCHAR);

    if ((UseLookaside == FALSE) || (Maximum > OBJECT_NAME_BUFFER_SIZE)) {

        //
        //  Attempt to allocate the buffer from nonpaged pool.
        //

        Buffer = ExAllocatePoolWithTag( OB_NAMESPACE_POOL_TYPE , Maximum, 'mNbO' );

    } else {

        //
        //  Attempt to allocate the name buffer from the lookaside list. If
        //  the allocation attempt fails, then attempt to allocate the name
        //  buffer from pool.
        //

        Maximum = OBJECT_NAME_BUFFER_SIZE;
        Buffer = ExAllocateFromPPLookasideList(LookasideNameBufferList);
    }

    //
    //  Initialize the string descriptor and return the buffer address.
    //

    ObjectName->Length = (USHORT)Length;
    ObjectName->MaximumLength = (USHORT)Maximum;
    ObjectName->Buffer = Buffer;

    return (PWCHAR)Buffer;
}


VOID
FASTCALL
ObpFreeObjectNameBuffer (
    OUT PUNICODE_STRING ObjectName
    )

/*++

Routine Description:

    This function frees an object name buffer.

    N.B. This function is nonpageable.

Arguments:

    ObjectName - Supplies a pointer to a name buffer string descriptor.

Return Value:

    None.

--*/

{
    PVOID Buffer;

    //
    //  If the size of the buffer is not equal to the size of lookaside list
    //  entries, then  free the buffer to pool. Otherwise, free the buffer to
    //  the lookaside list.
    //

    Buffer = ObjectName->Buffer;

    if (ObjectName->MaximumLength != OBJECT_NAME_BUFFER_SIZE) {
        ExFreePool(Buffer);

    } else {
        ExFreeToPPLookasideList(LookasideNameBufferList, Buffer);
    }

    return;
}


NTKERNELAPI
VOID
ObDeleteCapturedInsertInfo (
    __in PVOID Object
    )

/*++

Routine Description:

    This function frees the creation information that could be pointed at
    by the object header.

Arguments:

    Object - Supplies the object being modified

Return Value:

    None.

--*/

{
    POBJECT_HEADER ObjectHeader;

    PAGED_CODE();

    //
    //  Get the address of the object header and free the object create
    //  information object if the object is being created.
    //

    ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);

    if (ObjectHeader->Flags & OB_FLAG_NEW_OBJECT) {

        if (ObjectHeader->ObjectCreateInfo != NULL) {

            ObpFreeObjectCreateInformation(ObjectHeader->ObjectCreateInfo);

            ObjectHeader->ObjectCreateInfo = NULL;
        }
    }

    return;
}


NTSTATUS
ObpAllocateObject (
    IN POBJECT_CREATE_INFORMATION ObjectCreateInfo,
    IN KPROCESSOR_MODE OwnershipMode,
    IN POBJECT_TYPE ObjectType OPTIONAL,
    IN PUNICODE_STRING ObjectName,
    IN ULONG ObjectBodySize,
    OUT POBJECT_HEADER *ReturnedObjectHeader
    )

/*++

Routine Description:

    This routine allocates a new object including the object header
    and body from pool and fill in the appropriate fields.

Arguments:

    ObjectCreateInfo - Supplies the create information for the new object

    OwnershipMode - Supplies the processor mode of who is going to own
        the object

    ObjectType - Optionally supplies the object type of the object being
        created. If the object create info not null then this field must
        be supplied.

    ObjectName - Supplies the name of the object being created

    ObjectBodySize - Specifies the size, in bytes, of the body of the object
        being created

    ReturnedObjectHeader - Receives a pointer to the object header for the
        newly created objet.

Return Value:

    An appropriate status value.

--*/

{
    ULONG HeaderSize;
    POBJECT_HEADER ObjectHeader;
    ULONG QuotaInfoSize;
    ULONG HandleInfoSize;
    ULONG NameInfoSize;
    ULONG CreatorInfoSize;
    POBJECT_HEADER_QUOTA_INFO QuotaInfo;
    POBJECT_HEADER_HANDLE_INFO HandleInfo;
    POBJECT_HEADER_NAME_INFO NameInfo;
    POBJECT_HEADER_CREATOR_INFO CreatorInfo;
    POOL_TYPE PoolType;

    PAGED_CODE();

#if DBG
    ObpObjectsCreated += 1;
#endif // DBG

    //
    //  Compute the sizes of the optional object header components.
    //

    if (ObjectCreateInfo == NULL) {

        QuotaInfoSize = 0;
        HandleInfoSize = 0;
        NameInfoSize = sizeof( OBJECT_HEADER_NAME_INFO );
        CreatorInfoSize = sizeof( OBJECT_HEADER_CREATOR_INFO );

    } else {

        //
        //  The caller specified some additional object create info
        //
        //  First check to see if we need to set the quota
        //

        if (((ObjectCreateInfo->PagedPoolCharge != ObjectType->TypeInfo.DefaultPagedPoolCharge ||
              ObjectCreateInfo->NonPagedPoolCharge != ObjectType->TypeInfo.DefaultNonPagedPoolCharge ||
              ObjectCreateInfo->SecurityDescriptorCharge > SE_DEFAULT_SECURITY_QUOTA) &&
                 PsGetCurrentProcess() != PsInitialSystemProcess) ||
            (ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE)) {

            QuotaInfoSize = sizeof( OBJECT_HEADER_QUOTA_INFO );
#if DBG
            ObpObjectsWithPoolQuota += 1;
#endif // DBG

        } else {

            QuotaInfoSize = 0;
        }

        //
        //  Check if we are to allocate space to maintain handle counts
        //

        if (ObjectType->TypeInfo.MaintainHandleCount) {

            HandleInfoSize = sizeof( OBJECT_HEADER_HANDLE_INFO );
#if DBG
            ObpObjectsWithHandleDB += 1;
#endif // DBG

        } else {

            HandleInfoSize = 0;
        }

        //
        //  Check if we are to allocate space for the name
        //

        if (ObjectName->Buffer != NULL) {

            NameInfoSize = sizeof( OBJECT_HEADER_NAME_INFO );
#if DBG
            ObpObjectsWithName += 1;
#endif // DBG

        } else {

            NameInfoSize = 0;
        }

        //
        //  Finally check if we are to maintain the creator info
        //

        if (ObjectType->TypeInfo.MaintainTypeList) {

            CreatorInfoSize = sizeof( OBJECT_HEADER_CREATOR_INFO );
#if DBG
            ObpObjectsWithCreatorInfo += 1;
#endif // DBG

        } else {

            CreatorInfoSize = 0;
        }
    }

    //
    //  Now compute the total header size
    //

    HeaderSize = QuotaInfoSize +
                 HandleInfoSize +
                 NameInfoSize +
                 CreatorInfoSize +
                 FIELD_OFFSET( OBJECT_HEADER, Body );

    //
    //  Allocate and initialize the object.
    //
    //  If the object type is not specified or specifies nonpaged pool,
    //  then allocate the object from nonpaged pool.
    //  Otherwise, allocate the object from paged pool.
    //

    if ((ObjectType == NULL) || (ObjectType->TypeInfo.PoolType == NonPagedPool)) {

        PoolType = NonPagedPool;

    } else {

        PoolType = PagedPool;
    }

    ObjectHeader = ExAllocatePoolWithTag( PoolType,
                                          HeaderSize + ObjectBodySize,
                                          (ObjectType == NULL ? 'TjbO' : ObjectType->Key) |
                                            PROTECTED_POOL );

    if (ObjectHeader == NULL) {

        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    //  Now based on if we are to put in the quota, handle, name, or creator info we
    //  will do the extra work.  This order is very important because we rely on
    //  it to free the object.
    //

    if (QuotaInfoSize != 0) {

        QuotaInfo = (POBJECT_HEADER_QUOTA_INFO)ObjectHeader;
        QuotaInfo->PagedPoolCharge = ObjectCreateInfo->PagedPoolCharge;
        QuotaInfo->NonPagedPoolCharge = ObjectCreateInfo->NonPagedPoolCharge;
        QuotaInfo->SecurityDescriptorCharge = ObjectCreateInfo->SecurityDescriptorCharge;
        QuotaInfo->ExclusiveProcess = NULL;
        ObjectHeader = (POBJECT_HEADER)(QuotaInfo + 1);
    }

    if (HandleInfoSize != 0) {

        HandleInfo = (POBJECT_HEADER_HANDLE_INFO)ObjectHeader;
        HandleInfo->SingleEntry.HandleCount = 0;
        ObjectHeader = (POBJECT_HEADER)(HandleInfo + 1);
    }

    if (NameInfoSize != 0) {

        NameInfo = (POBJECT_HEADER_NAME_INFO)ObjectHeader;
        NameInfo->Name = *ObjectName;
        NameInfo->Directory = NULL;
        NameInfo->QueryReferences = 1;

        if ( (OwnershipMode == KernelMode) 
                && 
             (ObjectCreateInfo != NULL)
                &&
             (ObjectCreateInfo->Attributes & OBJ_KERNEL_EXCLUSIVE) ) {

            NameInfo->QueryReferences |= OBP_NAME_KERNEL_PROTECTED;
        }
       
        ObjectHeader = (POBJECT_HEADER)(NameInfo + 1);
    }

    if (CreatorInfoSize != 0) {

        CreatorInfo = (POBJECT_HEADER_CREATOR_INFO)ObjectHeader;
        CreatorInfo->CreatorBackTraceIndex = 0;
        CreatorInfo->CreatorUniqueProcess = PsGetCurrentProcess()->UniqueProcessId;
        InitializeListHead( &CreatorInfo->TypeList );

        PERFINFO_ADD_OBJECT_TO_ALLOCATED_TYPE_LIST(CreatorInfo, ObjectType);

        ObjectHeader = (POBJECT_HEADER)(CreatorInfo + 1);
    }

    //
    //  Compute the proper offsets based on what we have
    //

    if (QuotaInfoSize != 0) {

        ObjectHeader->QuotaInfoOffset = (UCHAR)(QuotaInfoSize + HandleInfoSize + NameInfoSize + CreatorInfoSize);

    } else {

        ObjectHeader->QuotaInfoOffset = 0;
    }

    if (HandleInfoSize != 0) {

        ObjectHeader->HandleInfoOffset = (UCHAR)(HandleInfoSize + NameInfoSize + CreatorInfoSize);

    } else {

        ObjectHeader->HandleInfoOffset = 0;
    }

    if (NameInfoSize != 0) {

        ObjectHeader->NameInfoOffset =  (UCHAR)(NameInfoSize + CreatorInfoSize);

    } else {

        ObjectHeader->NameInfoOffset = 0;
    }

    //
    //  Say that this is a new object, and conditionally set the other flags
    //

    ObjectHeader->Flags = OB_FLAG_NEW_OBJECT;

    if (CreatorInfoSize != 0) {

        ObjectHeader->Flags |= OB_FLAG_CREATOR_INFO;
    }

    if (HandleInfoSize != 0) {

        ObjectHeader->Flags |= OB_FLAG_SINGLE_HANDLE_ENTRY;
    }

    //
    //  Set the counters and its type
    //

    ObjectHeader->PointerCount = 1;
    ObjectHeader->HandleCount = 0;
    ObjectHeader->Type = ObjectType;

    //
    //  Initialize the object header.
    //
    //  N.B. The initialization of the object header is done field by
    //       field rather than zeroing the memory and then initializing
    //       the pertinent fields.
    //
    //  N.B. It is assumed that the caller will initialize the object
    //       attributes, object ownership, and parse context.
    //

    if (OwnershipMode == KernelMode) {

        ObjectHeader->Flags |= OB_FLAG_KERNEL_OBJECT;
    }

    if (ObjectCreateInfo != NULL &&
        ObjectCreateInfo->Attributes & OBJ_PERMANENT ) {

        ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT;
    }

    if ((ObjectCreateInfo != NULL) &&
        (ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE)) {

        ObjectHeader->Flags |= OB_FLAG_EXCLUSIVE_OBJECT;
    }

    ObjectHeader->ObjectCreateInfo = ObjectCreateInfo;
    ObjectHeader->SecurityDescriptor = NULL;

    if (ObjectType != NULL) {

        InterlockedIncrement((PLONG)&ObjectType->TotalNumberOfObjects);

        if (ObjectType->TotalNumberOfObjects > ObjectType->HighWaterNumberOfObjects) {

            ObjectType->HighWaterNumberOfObjects = ObjectType->TotalNumberOfObjects;
        }
    }

#if DBG

    //
    //  On a checked build echo out allocs
    //

    if (ObpShowAllocAndFree) {

        DbgPrint( "OB: Alloc %lx (%lx) %04lu", ObjectHeader, ObjectHeader, ObjectBodySize );

        if (ObjectType) {

            DbgPrint(" - %wZ\n", &ObjectType->Name );

        } else {

            DbgPrint(" - Type\n" );
        }
    }
#endif

    *ReturnedObjectHeader = ObjectHeader;

    return STATUS_SUCCESS;
}


VOID
FASTCALL
ObpFreeObject (
    IN PVOID Object
    )

/*++

Routine Description:

    This routine undoes ObpAllocateObject.  It returns the object back to free pool.

Arguments:

    Object - Supplies a pointer to the body of the object being freed.

Return Value:

    None.

--*/

{
    POBJECT_HEADER ObjectHeader;
    POBJECT_TYPE ObjectType;
    POBJECT_HEADER_QUOTA_INFO QuotaInfo;
    POBJECT_HEADER_HANDLE_INFO HandleInfo;
    POBJECT_HEADER_NAME_INFO NameInfo;
    POBJECT_HEADER_CREATOR_INFO CreatorInfo;
    PVOID FreeBuffer;
    ULONG NonPagedPoolCharge;
    ULONG PagedPoolCharge;

    PAGED_CODE();

    //
    //  Get the address of the object header.
    //

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

    //
    //  Now from the header determine the start of the allocation.  We need
    //  to backup based on what precedes the header.  The order is very
    //  important and must be the inverse of that used by ObpAllocateObject
    //

    FreeBuffer = ObjectHeader;

    CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO( ObjectHeader );

    if (CreatorInfo != NULL) {

        FreeBuffer = CreatorInfo;
    }

    NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );

    if (NameInfo != NULL) {

        FreeBuffer = NameInfo;
    }

    HandleInfo = OBJECT_HEADER_TO_HANDLE_INFO( ObjectHeader );

    if (HandleInfo != NULL) {

        FreeBuffer = HandleInfo;
    }

    QuotaInfo = OBJECT_HEADER_TO_QUOTA_INFO( ObjectHeader );

    if (QuotaInfo != NULL) {

        FreeBuffer = QuotaInfo;
    }

#if DBG

    //
    //  On a checked build echo out frees
    //

    if (ObpShowAllocAndFree) {

        DbgPrint( "OB: Free  %lx (%lx) - Type: %wZ\n", ObjectHeader, ObjectHeader, &ObjectType->Name );
    }
#endif

    //
    //  Decrement the number of objects of this type
    //

    InterlockedDecrement((PLONG)&ObjectType->TotalNumberOfObjects);

    //
    //  Check where we were in the object initialization phase.  This
    //  flag really only tests if we have charged quota for this object.
    //  This is because the object create info and the quota block charged
    //  are unioned together.
    //

    if (ObjectHeader->Flags & OB_FLAG_NEW_OBJECT) {

        if (ObjectHeader->ObjectCreateInfo != NULL) {

            ObpFreeObjectCreateInformation( ObjectHeader->ObjectCreateInfo );

            ObjectHeader->ObjectCreateInfo = NULL;
        }

    } else {

        if (ObjectHeader->QuotaBlockCharged != NULL) {

            if (QuotaInfo != NULL) {

                PagedPoolCharge = QuotaInfo->PagedPoolCharge +
                                  QuotaInfo->SecurityDescriptorCharge;

                NonPagedPoolCharge = QuotaInfo->NonPagedPoolCharge;

            } else {

                PagedPoolCharge = ObjectType->TypeInfo.DefaultPagedPoolCharge;

                if (ObjectHeader->Flags & OB_FLAG_DEFAULT_SECURITY_QUOTA ) {

                    PagedPoolCharge += SE_DEFAULT_SECURITY_QUOTA;
                }

                NonPagedPoolCharge = ObjectType->TypeInfo.DefaultNonPagedPoolCharge;
            }

            PsReturnSharedPoolQuota( ObjectHeader->QuotaBlockCharged,
                                     PagedPoolCharge,
                                     NonPagedPoolCharge );

            ObjectHeader->QuotaBlockCharged = NULL;
        }
    }

    if ((HandleInfo != NULL) &&
        ((ObjectHeader->Flags & OB_FLAG_SINGLE_HANDLE_ENTRY) == 0)) {

        //
        //  If a handle database has been allocated, then free the memory.
        //

        ExFreePool( HandleInfo->HandleCountDataBase );

        HandleInfo->HandleCountDataBase = NULL;
    }

    //
    //  If a name string buffer has been allocated, then free the memory.
    //

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

        ExFreePool( NameInfo->Name.Buffer );

        NameInfo->Name.Buffer = NULL;
    }

    PERFINFO_REMOVE_OBJECT_FROM_ALLOCATED_TYPE_LIST(CreatorInfo, ObjectHeader);

    //
    //  Trash type field so we don't get far if we attempt to
    //  use a stale object pointer to this object.
    //
    //  Win64 Note: trash it by zero-extended it. 
    //                sign-extension will create a valid kernel address.


    ObjectHeader->Type = UIntToPtr(0xBAD0B0B0); 
    ExFreePoolWithTag( FreeBuffer,
                       (ObjectType == NULL ? 'TjbO' : ObjectType->Key) |
                            PROTECTED_POOL );

    return;
}


VOID
FASTCALL
ObFreeObjectCreateInfoBuffer (
    IN POBJECT_CREATE_INFORMATION ObjectCreateInfo
    )

/*++

Routine Description:

    This function frees a create information buffer.  Called from IO component

    N.B. This function is nonpageable.

Arguments:

    ObjectCreateInfo - Supplies a pointer to a create information buffer.

Return Value:

    None.

--*/

{
    ObpFreeObjectCreateInfoBuffer( ObjectCreateInfo );

    return;
}

