/*++

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:

    obp.h

Abstract:

    Private include file for the OB subcomponent of the NTOS project

--*/

#pragma warning(disable:4214)   // bit field types other than int
#pragma warning(disable:4201)   // nameless struct/union
#pragma warning(disable:4324)   // alignment sensitive to declspec
#pragma warning(disable:4127)   // condition expression is constant
#pragma warning(disable:4115)   // named type definition in parentheses
#pragma warning(disable:4053)   // one void operand
#pragma warning(disable:4706)   // assignment within conditional

#include "ntos.h"
#include "seopaque.h"
#include <zwapi.h>

#ifdef _WIN64

#define ObpInterlockedExchangeAdd InterlockedExchangeAdd64
#define ObpInterlockedIncrement InterlockedIncrement64
#define ObpInterlockedDecrement InterlockedDecrement64
#define ObpInterlockedCompareExchange InterlockedCompareExchange64

#else 

#define ObpInterlockedExchangeAdd InterlockedExchangeAdd
#define ObpInterlockedIncrement InterlockedIncrement
#define ObpInterlockedDecrement InterlockedDecrement
#define ObpInterlockedCompareExchange InterlockedCompareExchange

#endif

#define OBP_PAGEDPOOL_NAMESPACE

//
//  The Object Header structures are private, but are defined in ob.h
//  so that various macros can directly access header fields.
//

struct _OBJECT_HEADER;
struct _OBJECT_BODY_HEADER;

//
//  Setup default pool tags
//

#ifdef POOL_TAGGING
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'  bO')
#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'  bO')
#endif

//
//  Define some macros that will verify that our callbacks don't give us a bad irql
//

#if DBG
#define ObpBeginTypeSpecificCallOut( IRQL ) (IRQL)=KeGetCurrentIrql()
#define ObpEndTypeSpecificCallOut( IRQL, str, ot, o ) {                                               \
    if ((IRQL)!=KeGetCurrentIrql()) {                                                                 \
        DbgPrint( "OB: ObjectType: %wZ  Procedure: %s  Object: %08x\n", &ot->Name, str, o );          \
        DbgPrint( "    Returned at %x IRQL, but was called at %x IRQL\n", KeGetCurrentIrql(), IRQL ); \
        DbgBreakPoint();                                                                              \
    }                                                                                                 \
}
#else
#define ObpBeginTypeSpecificCallOut( IRQL )
#define ObpEndTypeSpecificCallOut( IRQL, str, ot, o )
#endif // DBG

//
//  Define some more macros to validate the current irql
//

#if DBG
#define ObpValidateIrql( str ) \
    if (KeGetCurrentIrql() > APC_LEVEL) { \
        DbgPrint( "OB: %s called at IRQL %d\n", (str), KeGetCurrentIrql() ); \
        DbgBreakPoint(); \
        }
#else
#define ObpValidateIrql( str )
#endif // DBG


//
//  This global lock protects the granted access lists when we are collecting stack traces
//

EX_PUSH_LOCK ObpLock;

KEVENT ObpDefaultObject;
WORK_QUEUE_ITEM ObpRemoveObjectWorkItem;
PVOID ObpRemoveObjectList;

//
//  This global lock is used to protect the device map tear down and build up
//  We can no longer use an individual lock in the device map itself because
//  that wasn't sufficient to protect the device map itself.
//

#ifdef OBP_PAGEDPOOL_NAMESPACE

#define OB_NAMESPACE_POOL_TYPE PagedPool

KGUARDED_MUTEX ObpDeviceMapLock;

#define OBP_DECLARE_OLDIRQL

#define ObpLockDeviceMap() \
    KeAcquireGuardedMutex( &ObpDeviceMapLock )

#define ObpUnlockDeviceMap() \
    KeReleaseGuardedMutex( &ObpDeviceMapLock )

#else // !OBP_PAGEDPOOL_NAMESPACE

#define OB_NAMESPACE_POOL_TYPE NonPagedPool

KSPIN_LOCK ObpDeviceMapLock;

#define ObpLockDeviceMap() \
    ExAcquireSpinLock( &ObpDeviceMapLock, &OldIrql )

#define ObpUnlockDeviceMap() \
    ExReleaseSpinLock( &ObpDeviceMapLock, OldIrql )

#endif  // OBP_PAGEDPOOL_NAMESPACE

#define ObpIncrPointerCountEx(np,Count) (ObpInterlockedExchangeAdd (&np->PointerCount, Count) + Count)
#define ObpDecrPointerCountEx(np,Count) (ObpInterlockedExchangeAdd (&np->PointerCount, -(LONG_PTR)(Count)) - Count)

#ifndef POOL_TAGGING

#define ObpIncrPointerCount(np)           ObpInterlockedIncrement( &np->PointerCount )
#define ObpDecrPointerCount(np)           ObpInterlockedDecrement( &np->PointerCount )
#define ObpDecrPointerCountWithResult(np) (ObpInterlockedDecrement( &np->PointerCount ) == 0)

#define ObpPushStackInfo(np, inc)

#else //POOL_TAGGING

VOID
ObpInitStackTrace();

VOID
ObpRegisterObject (
    POBJECT_HEADER ObjectHeader
    );

VOID
ObpDeregisterObject (
    POBJECT_HEADER ObjectHeader
    );

VOID
ObpPushStackInfo (
    POBJECT_HEADER ObjectHeader,
    BOOLEAN IsRef
    );

extern BOOLEAN ObpTraceEnabled;

#define ObpIncrPointerCount(np)           ((ObpTraceEnabled ? ObpPushStackInfo(np,TRUE) : 0),ObpInterlockedIncrement( &np->PointerCount ))
#define ObpDecrPointerCount(np)           ((ObpTraceEnabled ? ObpPushStackInfo(np,FALSE) : 0),ObpInterlockedDecrement( &np->PointerCount ))
#define ObpDecrPointerCountWithResult(np) ((ObpTraceEnabled ? ObpPushStackInfo(np,FALSE) : 0),(ObpInterlockedDecrement( &np->PointerCount ) == 0))

#endif //POOL_TAGGING

#define ObpIncrHandleCount(np)            ObpInterlockedIncrement( &np->HandleCount )
#define ObpDecrHandleCount(np)            (ObpInterlockedDecrement( &np->HandleCount ) == 0)



//
//  Define macros to acquire and release an object type fast mutex.
//
//
//  VOID
//  ObpEnterObjectTypeMutex (
//      IN POBJECT_TYPE ObjectType
//      )
//

#define ObpEnterObjectTypeMutex(_ObjectType) {                   \
    ObpValidateIrql("ObpEnterObjectTypeMutex");                  \
    KeEnterCriticalRegion();                                     \
    ExAcquireResourceExclusiveLite(&(_ObjectType)->Mutex, TRUE); \
}

//
//  VOID
//  ObpLeaveObjectTypeMutex (
//      IN POBJECT_TYPE ObjectType
//      )
//

#define ObpLeaveObjectTypeMutex(_ObjectType) {  \
    ExReleaseResourceLite(&(_ObjectType)->Mutex);   \
    KeLeaveCriticalRegion();                    \
    ObpValidateIrql("ObpLeaveObjectTypeMutex"); \
}

#define LOCK_HASH_MASK (OBJECT_LOCK_COUNT - 1)

#define ObpHashObjectHeader(_ObjectHeader)      \
    ((((ULONG_PTR)(_ObjectHeader)) >> 8) & LOCK_HASH_MASK)

#define ObpLockObject(_ObjectHeader) {                           \
    ULONG_PTR LockIndex = ObpHashObjectHeader(_ObjectHeader);    \
    ObpValidateIrql("ObpEnterObjectTypeMutex");                  \
    KeEnterCriticalRegion();                                     \
    ExAcquireResourceExclusiveLite(&((_ObjectHeader)->Type->ObjectLocks[LockIndex]), TRUE); \
}

#define ObpLockObjectShared(_ObjectHeader) {                     \
    ULONG_PTR LockIndex = ObpHashObjectHeader(_ObjectHeader);    \
    ObpValidateIrql("ObpEnterObjectTypeMutex");                  \
    KeEnterCriticalRegion();                                     \
    ExAcquireResourceSharedLite(&((_ObjectHeader)->Type->ObjectLocks[LockIndex]), TRUE); \
}

#define ObpUnlockObject(_ObjectHeader) {                                        \
    ULONG_PTR LockIndex = ObpHashObjectHeader(_ObjectHeader);                   \
    ExReleaseResourceLite(&((_ObjectHeader)->Type->ObjectLocks[LockIndex]));        \
    KeLeaveCriticalRegion();                                                    \
    ObpValidateIrql("ObpLeaveObjectTypeMutex");                                 \
}


//
//  A Macro to return the object table for the current process
//

#define ObpGetObjectTable() (PsGetCurrentProcess()->ObjectTable)

//
//  Macro to test whether or not the object manager is responsible for
//  an object's security descriptor, or if the object has its own
//  security management routines.
//

#define ObpCentralizedSecurity(_ObjectType)                              \
    ((_ObjectType)->TypeInfo.SecurityProcedure == SeDefaultObjectMethod)

//
//  Declare a global table of object types.
//

#define OBP_MAX_DEFINED_OBJECT_TYPES 48
POBJECT_TYPE ObpObjectTypes[ OBP_MAX_DEFINED_OBJECT_TYPES ];


//
//  This is some special purpose code to keep a table of access masks correlated with
//  back traces.  If used these routines replace the GrantedAccess mask in the
//  preceding object table entry with a granted access index and a call back index.
//

#if i386 
ACCESS_MASK
ObpTranslateGrantedAccessIndex (
    USHORT GrantedAccessIndex
    );

USHORT
ObpComputeGrantedAccessIndex (
    ACCESS_MASK GrantedAccess
    );

USHORT ObpCurCachedGrantedAccessIndex;
USHORT ObpMaxCachedGrantedAccessIndex;
PACCESS_MASK ObpCachedGrantedAccesses;
#endif // i386 

//
//  The three low order bits of the object table entry are used for handle
//  attributes.
//

//
//  We moved the PROTECT_CLOSE in the granted access mask
//

extern ULONG ObpAccessProtectCloseBit;

//
//  The bit mask for inherit MUST be 0x2.
//

#if (OBJ_INHERIT != 0x2)

#error Object inheritance bit definition conflicts

#endif

//
//  Define the bit mask for the generate audit on close attribute.
//
//  When a handle to an object with security is created, audit routines will
//  be called to perform any auditing that may be required. The audit routines
//  will return a boolean indicating whether or not audits should be generated
//  on close.
//

#define OBJ_AUDIT_OBJECT_CLOSE 0x00000004L


//
//  The following three bits are available for handle attributes in the
//  Object field of an ObjectTableEntry.
//

#define OBJ_HANDLE_ATTRIBUTES (OBJ_PROTECT_CLOSE | OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)

#define ObpDecodeGrantedAccess(Access)       \
    ((Access) & ~ObpAccessProtectCloseBit)

#define ObpGetHandleAttributes(HandleTableEntry)                                    \
     ((((HandleTableEntry)->GrantedAccess) & ObpAccessProtectCloseBit) ?            \
     ((((HandleTableEntry)->ObAttributes) & OBJ_HANDLE_ATTRIBUTES) | OBJ_PROTECT_CLOSE) : \
     (((HandleTableEntry)->ObAttributes) & (OBJ_INHERIT | OBJ_AUDIT_OBJECT_CLOSE)) )

#define ObpEncodeProtectClose(ObjectTableEntry)                         \
    if ( (ObjectTableEntry).ObAttributes & OBJ_PROTECT_CLOSE ) {        \
                                                                        \
        (ObjectTableEntry).ObAttributes &= ~OBJ_PROTECT_CLOSE;          \
        (ObjectTableEntry).GrantedAccess |= ObpAccessProtectCloseBit;   \
    }


//
//  Security Descriptor Cache
//
//  Cache entry header.
//

typedef struct _SECURITY_DESCRIPTOR_HEADER {

    LIST_ENTRY Link;
    ULONG  RefCount;
    ULONG  FullHash;
#if defined (_WIN64)
    PVOID  Spare;   // Align to 16 byte boundary.
#endif
    QUAD   SecurityDescriptor;

} SECURITY_DESCRIPTOR_HEADER, *PSECURITY_DESCRIPTOR_HEADER;

//
//  Macro to convert a security descriptor into its security descriptor header
//

#define SD_TO_SD_HEADER(_sd) \
    CONTAINING_RECORD( (_sd), SECURITY_DESCRIPTOR_HEADER, SecurityDescriptor )

//
//  Macro to convert a header link into its security descriptor header
//

#define LINK_TO_SD_HEADER(_link) \
    CONTAINING_RECORD( (_link), SECURITY_DESCRIPTOR_HEADER, Link )


//
//  Number of minor hash entries
//

#define SECURITY_DESCRIPTOR_CACHE_ENTRIES    257



//
//  Lock state signatures
//

#define OBP_LOCK_WAITEXCLUSIVE_SIGNATURE    0xAAAA1234
#define OBP_LOCK_WAITSHARED_SIGNATURE       0xBBBB1234
#define OBP_LOCK_OWNEDEXCLUSIVE_SIGNATURE   0xCCCC1234
#define OBP_LOCK_OWNEDSHARED_SIGNATURE      0xDDDD1234
#define OBP_LOCK_RELEASED_SIGNATURE         0xEEEE1234
#define OBP_LOCK_UNUSED_SIGNATURE           0xFFFF1234

//
//  Lookup directories
//

typedef struct _OBP_LOOKUP_CONTEXT {

    POBJECT_DIRECTORY Directory;
    PVOID Object;
    ULONG HashValue;
    USHORT HashIndex;
    BOOLEAN DirectoryLocked;
    volatile ULONG LockStateSignature;

} OBP_LOOKUP_CONTEXT, *POBP_LOOKUP_CONTEXT;

//
// Context for the sweep routine. Passed through handle enumeration.
//
typedef struct _OBP_SWEEP_CONTEXT {
    PHANDLE_TABLE HandleTable;
    KPROCESSOR_MODE PreviousMode;
} OBP_SWEEP_CONTEXT, *POBP_SWEEP_CONTEXT;


//
//  Global data
//

POBJECT_TYPE ObpTypeObjectType;
POBJECT_TYPE ObpDirectoryObjectType;
POBJECT_TYPE ObpSymbolicLinkObjectType;
POBJECT_TYPE ObpDeviceMapObjectType;
POBJECT_DIRECTORY ObpRootDirectoryObject;
POBJECT_DIRECTORY ObpTypeDirectoryObject;

typedef union {
    WCHAR Name[sizeof(ULARGE_INTEGER)/sizeof(WCHAR)];
    ULARGE_INTEGER Alignment;
} ALIGNEDNAME;

extern const ALIGNEDNAME ObpDosDevicesShortNamePrefix;
extern const ALIGNEDNAME ObpDosDevicesShortNameRoot;
extern const UNICODE_STRING ObpDosDevicesShortName;

ERESOURCE SecurityDescriptorCacheLock;

//
//  Define date structures for the object creation information region.
//

extern GENERAL_LOOKASIDE ObpCreateInfoLookasideList;

//
//  Define data structures for the object name buffer lookaside list.
//

#define OBJECT_NAME_BUFFER_SIZE 248

extern GENERAL_LOOKASIDE ObpNameBufferLookasideList;

//
//  There is one global kernel handle table accessed via negative handle
//  and only in kernel mode
//

PHANDLE_TABLE ObpKernelHandleTable;

//
//  The following macros are used to test and manipulate special kernel
//  handles.  A kernel handle is just a regular handle with its sign
//  bit set.  But must exclude -1 and -2 values which are the current
//  process and current thread constants.
//

#define KERNEL_HANDLE_MASK ((ULONG_PTR)((LONG)0x80000000))

#define IsKernelHandle(H,M)                                \
    (((KERNEL_HANDLE_MASK & (ULONG_PTR)(H)) == KERNEL_HANDLE_MASK) && \
     ((M) == KernelMode) &&                                \
     ((H) != NtCurrentThread()) &&                         \
     ((H) != NtCurrentProcess()))

#define EncodeKernelHandle(H) (HANDLE)(KERNEL_HANDLE_MASK | (ULONG_PTR)(H))

#define DecodeKernelHandle(H) (HANDLE)(KERNEL_HANDLE_MASK ^ (ULONG_PTR)(H))

//
//  Test macro for overflow
//

#define ObpIsOverflow(A,B) ((A) > ((A) + (B)))


//
//  Internal Entry Points defined in obcreate.c and some associated macros
//

#define ObpFreeObjectCreateInformation(_ObjectCreateInfo) { \
    ObpReleaseObjectCreateInformation((_ObjectCreateInfo)); \
    ObpFreeObjectCreateInfoBuffer((_ObjectCreateInfo));     \
}

#define ObpReleaseObjectCreateInformation(_ObjectCreateInfo) {               \
    if ((_ObjectCreateInfo)->SecurityDescriptor != NULL) {                   \
        SeReleaseSecurityDescriptor((_ObjectCreateInfo)->SecurityDescriptor, \
                                    (_ObjectCreateInfo)->ProbeMode,          \
                                     TRUE);                                  \
        (_ObjectCreateInfo)->SecurityDescriptor = NULL;                      \
    }                                                                        \
}

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

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

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

VOID
FASTCALL
ObpFreeObjectNameBuffer (
    IN PUNICODE_STRING ObjectName
    );

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


VOID
FASTCALL
ObpFreeObject (
    IN PVOID Object
    );


/*++

POBJECT_CREATE_INFORMATION
ObpAllocateObjectCreateInfoBuffer (
    VOID
    )

Routine Description:

    This function allocates a created information buffer.

    N.B. This function is nonpageable.

Arguments:

    None.

Return Value:

    If the allocation is successful, then the address of the allocated
    create information buffer is is returned as the function value.
    Otherwise, a value of NULL is returned.

--*/

#define ObpAllocateObjectCreateInfoBuffer()             \
    (POBJECT_CREATE_INFORMATION)ExAllocateFromPPLookasideList(LookasideCreateInfoList)


/*++

VOID
FASTCALL
ObpFreeObjectCreateInfoBuffer (
    IN POBJECT_CREATE_INFORMATION ObjectCreateInfo
    )

Routine Description:

    This function frees a create information buffer.

    N.B. This function is nonpageable.

Arguments:

    ObjectCreateInfo - Supplies a pointer to a create information buffer.

Return Value:

    None.

--*/

#define ObpFreeObjectCreateInfoBuffer(ObjectCreateInfo) \
    ExFreeToPPLookasideList(LookasideCreateInfoList, ObjectCreateInfo)

//
//  Internal Entry Points defined in oblink.c
//

NTSTATUS
ObpParseSymbolicLink (
    IN PVOID ParseObject,
    IN PVOID ObjectType,
    IN PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE AccessMode,
    IN ULONG Attributes,
    IN OUT PUNICODE_STRING CompleteName,
    IN OUT PUNICODE_STRING RemainingName,
    IN OUT PVOID Context OPTIONAL,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    OUT PVOID *Object
    );

VOID
ObpDeleteSymbolicLink (
    IN  PVOID   Object
    );

VOID
ObpCreateSymbolicLinkName (
    POBJECT_SYMBOLIC_LINK SymbolicLink
    );

VOID
ObpDeleteSymbolicLinkName (
    POBJECT_SYMBOLIC_LINK SymbolicLink
    );


//
//  Internal Entry Points defined in obdir.c
//

PVOID
ObpLookupDirectoryEntry (
    IN POBJECT_DIRECTORY Directory,
    IN PUNICODE_STRING Name,
    IN ULONG Attributes,
    IN BOOLEAN SearchShadow,
    OUT POBP_LOOKUP_CONTEXT LookupContext
    );


BOOLEAN
ObpInsertDirectoryEntry (
    IN POBJECT_DIRECTORY Directory,
    IN POBP_LOOKUP_CONTEXT LookupContext,
    IN POBJECT_HEADER ObjectHeader
    );


BOOLEAN
ObpDeleteDirectoryEntry (
    IN POBP_LOOKUP_CONTEXT LookupContext
    );


NTSTATUS
ObpLookupObjectName (
    IN HANDLE RootDirectoryHandle,
    IN PUNICODE_STRING ObjectName,
    IN ULONG Attributes,
    IN POBJECT_TYPE ObjectType,
    IN KPROCESSOR_MODE AccessMode,
    IN PVOID ParseContext OPTIONAL,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    IN PVOID InsertObject OPTIONAL,
    IN OUT PACCESS_STATE AccessState,
    OUT POBP_LOOKUP_CONTEXT LookupContext,
    OUT PVOID *FoundObject
    );

VOID
ObpUnlockObjectDirectoryPath (
    IN POBJECT_DIRECTORY LockedDirectory
    );

PDEVICE_MAP
ObpReferenceDeviceMap(
    );

VOID
FASTCALL
ObfDereferenceDeviceMap(
    IN PDEVICE_MAP DeviceMap
    );


//
//  Internal entry points defined in obref.c
//


VOID
ObpDeleteNameCheck (
    IN PVOID Object
    );


VOID
ObpProcessRemoveObjectQueue (
    PVOID Parameter
    );

VOID
ObpRemoveObjectRoutine (
    IN  PVOID   Object,
    IN  BOOLEAN CalledOnWorkerThread
    );


//
//  Internal entry points defined in obhandle.c
//


POBJECT_HANDLE_COUNT_ENTRY
ObpInsertHandleCount (
    POBJECT_HEADER ObjectHeader
    );

NTSTATUS
ObpIncrementHandleCount (
    OB_OPEN_REASON OpenReason,
    PEPROCESS Process,
    PVOID Object,
    POBJECT_TYPE ObjectType,
    PACCESS_STATE AccessState OPTIONAL,
    KPROCESSOR_MODE AccessMode,
    ULONG Attributes
    );


VOID
ObpDecrementHandleCount (
    PEPROCESS Process,
    POBJECT_HEADER ObjectHeader,
    POBJECT_TYPE ObjectType,
    ACCESS_MASK GrantedAccess
    );

NTSTATUS
ObpCreateHandle (
    IN OB_OPEN_REASON OpenReason,
    IN PVOID Object,
    IN POBJECT_TYPE ExpectedObjectType OPTIONAL,
    IN PACCESS_STATE AccessState,
    IN ULONG ObjectPointerBias OPTIONAL,
    IN ULONG Attributes,
    IN POBP_LOOKUP_CONTEXT LookupContext,
    IN KPROCESSOR_MODE AccessMode,
    OUT PVOID *ReferencedNewObject OPTIONAL,
    OUT PHANDLE Handle
    );

NTSTATUS
ObpIncrementUnnamedHandleCount (
    PACCESS_MASK DesiredAccess,
    PEPROCESS Process,
    PVOID Object,
    POBJECT_TYPE ObjectType,
    KPROCESSOR_MODE AccessMode,
    ULONG Attributes
    );


NTSTATUS
ObpCreateUnnamedHandle (
    IN PVOID Object,
    IN ACCESS_MASK DesiredAccess,
    IN ULONG ObjectPointerBias OPTIONAL,
    IN ULONG Attributes,
    IN KPROCESSOR_MODE AccessMode,
    OUT PVOID *ReferencedNewObject OPTIONAL,
    OUT PHANDLE Handle
    );

NTSTATUS
ObpChargeQuotaForObject (
    IN POBJECT_HEADER ObjectHeader,
    IN POBJECT_TYPE ObjectType,
    OUT PBOOLEAN NewObject
    );

NTSTATUS
ObpValidateDesiredAccess (
    IN ACCESS_MASK DesiredAccess
    );


//
//  Internal entry points defined in obse.c
//

BOOLEAN
ObpCheckPseudoHandleAccess (
    IN PVOID Object,
    IN ACCESS_MASK DesiredAccess,
    OUT PNTSTATUS AccessStatus,
    IN BOOLEAN TypeMutexLocked
    );


BOOLEAN
ObpCheckTraverseAccess (
    IN PVOID DirectoryObject,
    IN ACCESS_MASK TraverseAccess,
    IN PACCESS_STATE AccessState,
    IN BOOLEAN TypeMutexLocked,
    IN KPROCESSOR_MODE PreviousMode,
    OUT PNTSTATUS AccessStatus
    );

BOOLEAN
ObpCheckObjectReference (
    IN PVOID Object,
    IN OUT PACCESS_STATE AccessState,
    IN BOOLEAN TypeMutexLocked,
    IN KPROCESSOR_MODE AccessMode,
    OUT PNTSTATUS AccessStatus
    );


//
//  Internal entry points defined in obsdata.c
//

NTSTATUS
ObpInitSecurityDescriptorCache (
    VOID
    );

ULONG
ObpHashSecurityDescriptor (
    PSECURITY_DESCRIPTOR SecurityDescriptor,
    ULONG Length
    );

ULONG
ObpHashBuffer (
    PVOID Data,
    ULONG Length
    );

PSECURITY_DESCRIPTOR_HEADER
ObpCreateCacheEntry (
    PSECURITY_DESCRIPTOR InputSecurityDescriptor,
    ULONG Length,
    ULONG FullHash,
    ULONG RefBias
    );


PSECURITY_DESCRIPTOR
ObpReferenceSecurityDescriptor (
    POBJECT_HEADER ObjectHeader
    );


PVOID
ObpDestroySecurityDescriptorHeader (
    IN PSECURITY_DESCRIPTOR_HEADER Header
    );

BOOLEAN
ObpCompareSecurityDescriptors (
    IN PSECURITY_DESCRIPTOR SD1,
    ULONG Length,
    IN PSECURITY_DESCRIPTOR SD2
    );

NTSTATUS
ObpValidateAccessMask (
    PACCESS_STATE AccessState
    );

NTSTATUS
ObpCloseHandleTableEntry (
    IN PHANDLE_TABLE ObjectTable,
    IN PHANDLE_TABLE_ENTRY ObjectTableEntry,
    IN HANDLE Handle,
    IN KPROCESSOR_MODE PreviousMode,
    IN BOOLEAN Rundown
    );

NTSTATUS
ObpCloseHandle (
    IN HANDLE Handle,
    IN KPROCESSOR_MODE PreviousMode
    );

VOID
ObpDeleteObjectType (
    IN  PVOID   Object
    );

VOID
ObpAuditObjectAccess(
    IN HANDLE Handle,
    IN PHANDLE_TABLE_ENTRY_INFO ObjectTableEntryInfo,
    IN PUNICODE_STRING ObjectTypeName,
    IN ACCESS_MASK DesiredAccess
    );

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



//
//  Inline functions
//

FORCEINLINE
BOOLEAN
ObpSafeInterlockedIncrement(
    IN OUT LONG_PTR volatile *lpValue
    )

/*

Routine Description:

    This function increments the LONG value passed in.
    Unlike the InterlockedIncrement function, this will not increment from 0 to 1.
    It will return FALSE if it's trying to reference from 0.

Arguments:

    lpValue - The pointer to the LONG value that should be safe incremented

Return Value:

    Returns FALSE if the current value is 0 (so it cannot increment to 1). TRUE means the LONG
    value was incremented

*/

{
    LONG_PTR PointerCount, NewPointerCount;

    //
    // If the object is being deleted then the reference count is zero. So the idea here is to reference
    // the long value but avoid the 0 -> 1 transition that would cause a double delete.
    //

    PointerCount = ReadForWriteAccess(lpValue);

    do {
        if (PointerCount == 0) {
            return FALSE;
        }
        NewPointerCount = ObpInterlockedCompareExchange (lpValue,
                                                         PointerCount+1,
                                                         PointerCount);

        //
        // If the exchange compare completed ok then we did a reference so return true.
        //

        if (NewPointerCount == PointerCount) {

            return TRUE;
        }

        //
        // We failed because somebody else got in and changed the refence count on us. Use the new value to
        // prime the exchange again.
        //

        PointerCount = NewPointerCount;
    } while (TRUE);

    return TRUE;
}

#define OBP_NAME_LOCKED	            ((LONG)0x80000000)
#define OBP_NAME_KERNEL_PROTECTED   ((LONG)0x40000000)

FORCEINLINE
BOOLEAN
ObpSafeInterlockedIncrementLong(
    IN OUT LONG volatile *lpValue
    )

/*

Routine Description:

    This function increments the LONG value passed in.
    Unlike the InterlockedIncrement function, this will not increment from 0 to 1.
    It will return FALSE if it's trying to reference from 0.

Arguments:

    lpValue - The pointer to the LONG value that should be safe incremented

Return Value:

    Returns FALSE if the current value is 0 (so it cannot increment to 1). TRUE means the LONG
    value was incremented

*/

{
    LONG PointerCount, NewPointerCount;

    //
    // If the object is being deleted then the reference count is zero. So the idea here is to reference
    // the long value but avoid the 0 -> 1 transition that would cause a double delete.
    //

    PointerCount = ReadForWriteAccess(lpValue);

    do {
        if (PointerCount == 0) {
            return FALSE;
        }
        NewPointerCount = InterlockedCompareExchange (lpValue,
                                                      PointerCount+1,
                                                      PointerCount);

        //
        // If the exchange compare completed ok then we did a reference so return true.
        //

        if (NewPointerCount == PointerCount) {

            return TRUE;
        }

        //
        // We failed because somebody else got in and changed the refence count on us. Use the new value to
        // prime the exchange again.
        //

        PointerCount = NewPointerCount;
    } while (TRUE);

    return TRUE;
}


POBJECT_HEADER_NAME_INFO
FORCEINLINE
ObpReferenceNameInfo(
    IN POBJECT_HEADER ObjectHeader
    )

/*

Routine Description:
    This function references the name information structure. This is a substitute
    for the previous global locking mechanism that used the RootDirectoryMutex to protect
    the fields inside the NAME_INFO as well.
    If the function returns a non-NULL name info, the name buffer and Directory will not go away
    until the ObpDereferenceNameInfo call.

Arguments:

    ObjectHeader - The object header whose name should be safe-referenced

Return Value:
    Returns NULL if the object doesn't have a name information structure, or if the name info is being deleted
    Returns a pointer to the POBJECT_HEADER_NAME_INFO if it's safe to use the fields inside it.

*/

{
    POBJECT_HEADER_NAME_INFO NameInfo;
    NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );

    if ((NameInfo != NULL)
            &&
        ObpSafeInterlockedIncrementLong((PLONG) &NameInfo->QueryReferences )) {

        if (NameInfo->QueryReferences & OBP_NAME_LOCKED) {

            //
            //  The name is locked this means that the directory entry must be valid
            //

            ExAcquireReleasePushLockExclusive(&NameInfo->Directory->Lock);
        }

        return NameInfo;
    }

    return NULL;
}


VOID
FORCEINLINE
ObpDereferenceNameInfo(
    IN POBJECT_HEADER_NAME_INFO NameInfo
    )

/*

Routine Description:
    This function dereferences the name information structure. If the number of references
    drops to 0, the name is freed and the directory dereferenced

Arguments:

    NameInfo - The pointer to the name info structure, as returned by ObpReferenceNameInfo.
    (NULL value is allowed)

Return Value:
    None

*/

{

    if ( (NameInfo != NULL)
            &&
         (InterlockedDecrement((PLONG)&NameInfo->QueryReferences) == 0)) {

        PVOID DirObject;

        //
        //  Free the name buffer and zero out the name data fields
        //

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

            ExFreePool( NameInfo->Name.Buffer );

            NameInfo->Name.Buffer = NULL;
            NameInfo->Name.Length = 0;
            NameInfo->Name.MaximumLength = 0;
        }

        DirObject = NameInfo->Directory;

        if (DirObject != NULL) {

            NameInfo->Directory = NULL;
            ObDereferenceObjectDeferDelete( DirObject );
        }
    }
}


FORCEINLINE
LOGICAL
ObpIsKernelExclusiveObject(
    IN POBJECT_HEADER ObjectHeader
    )

/*

Routine Description:
    This function verifies if a named object can only be opened in kernel mode

Arguments:

    ObjectHeader - The object header whose name should be safe-referenced

Return Value:
    TRUE if the object is available only to kernel

*/

{
    POBJECT_HEADER_NAME_INFO NameInfo;
    NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );

    return ((NameInfo != NULL) && ((NameInfo->QueryReferences & OBP_NAME_KERNEL_PROTECTED) != 0));
}


VOID
FORCEINLINE
ObpLockDirectoryExclusive(
    IN POBJECT_DIRECTORY Directory,
    IN POBP_LOOKUP_CONTEXT LockContext
    )

/*

Routine Description:
    This Function locks exclusively the Directory. It is used for write access to
    the directory structure.

Arguments:

    Directory - The directory being locked

Return Value:
    None

*/

{
    LockContext->LockStateSignature = OBP_LOCK_WAITEXCLUSIVE_SIGNATURE;
    KeEnterCriticalRegion();
    ExAcquirePushLockExclusive( &Directory->Lock );
    LockContext->LockStateSignature = OBP_LOCK_OWNEDEXCLUSIVE_SIGNATURE;
}


VOID
FORCEINLINE
ObpLockDirectoryShared (
    IN POBJECT_DIRECTORY Directory,
    IN POBP_LOOKUP_CONTEXT LockContext
    )

/*

Routine Description:
    This Function locks shared the Directory. It is used to read fields inside
    the directory structure.

Arguments:

    Directory - The directory being locked

Return Value:
    None

*/

{
    LockContext->LockStateSignature = OBP_LOCK_WAITSHARED_SIGNATURE;
    KeEnterCriticalRegion();
    ExAcquirePushLockShared( &Directory->Lock );
    LockContext->LockStateSignature = OBP_LOCK_OWNEDSHARED_SIGNATURE;
}


VOID
FORCEINLINE
ObpUnlockDirectory(
    IN POBJECT_DIRECTORY Directory,
    IN POBP_LOOKUP_CONTEXT LockContext
    )

/*

Routine Description:
    This Function unlocks a Directory (previously locked exclusive or shared).

Arguments:

    Directory - The directory that needs to be unlocked

Return Value:
    None

*/

{
    ExReleasePushLock( &Directory->Lock );
    LockContext->LockStateSignature = OBP_LOCK_RELEASED_SIGNATURE;
    KeLeaveCriticalRegion();
}


VOID
FORCEINLINE
ObpInitializeLookupContext(
    IN POBP_LOOKUP_CONTEXT LookupContext
    )

/*

Routine Description:
    This Function initialize a lookup context structure.

Arguments:

    LookupContext - The LookupContext to be initialized

Return Value:
    None

*/

{
    LookupContext->DirectoryLocked = FALSE;
    LookupContext->Object = NULL;
    LookupContext->Directory = NULL;
    LookupContext->LockStateSignature = OBP_LOCK_UNUSED_SIGNATURE;
}


VOID
FORCEINLINE
ObpLockLookupContext (
    IN POBP_LOOKUP_CONTEXT LookupContext,
    IN POBJECT_DIRECTORY Directory
    )

/*

Routine Description:
    This function locks a lookup context. The directory
    is exclusively owned after this call and it's safe to access
    the directory in write mode. This function is intended to be used in
    insertion / deletion into/from the specified directory.

    The directory is unlocked at the next ObpReleaseLookupContext.

Arguments:

    LookupContext - The LookupContext to be initialized

    Directory - The directory being locked for exclusive access.

Return Value:
    None

*/

{

    ObpLockDirectoryExclusive(Directory, LookupContext);
    LookupContext->DirectoryLocked = TRUE;
    LookupContext->Directory = Directory;
}



VOID
FORCEINLINE
ObpReleaseLookupContext (
    IN POBP_LOOKUP_CONTEXT LookupContext
    )

/*

Routine Description:
    This function undoes the references and locking changes during these calls:
    ObpLockLookupContext and ObpLookupDirectoryEntry.

    N.B. If ObpLookupDirectoryEntry is called several times in a loop, each call
    will undo the references done at the previous call. ObpReleaseLookupContext
    should be called only ones at the end.

Arguments:

    LookupContext - The LookupContext to be released

Return Value:
    None

*/

{
    //
    //  If the context was locked we need to unlock the directory
    //

    if (LookupContext->DirectoryLocked) {

        ObpUnlockDirectory( LookupContext->Directory, LookupContext );
        LookupContext->Directory = NULL;
        LookupContext->DirectoryLocked = FALSE;
    }

    //
    //  Remove the references added to the name info and object
    //

    if (LookupContext->Object) {
        POBJECT_HEADER_NAME_INFO NameInfo;

        NameInfo = OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(LookupContext->Object));

        ObpDereferenceNameInfo( NameInfo );
        ObDereferenceObject(LookupContext->Object);
        LookupContext->Object = NULL;
    }
}

NTSTATUS
ObpReferenceProcessObjectByHandle (
    IN HANDLE Handle,
    IN PEPROCESS Process,
    IN PHANDLE_TABLE HandleTable,
    IN KPROCESSOR_MODE AccessMode,
    OUT PVOID *Object,
    OUT POBJECT_HANDLE_INFORMATION HandleInformation,
    OUT PACCESS_MASK AuditMask
    );

VOID
FORCEINLINE
ObpLockAllObjects (
    IN POBJECT_TYPE ObjectType
    )
{
    LONG i;

    KeEnterCriticalRegion();                                    

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

        ExAcquireResourceExclusiveLite(&(ObjectType->ObjectLocks[i]), TRUE);
    }
}

VOID
FORCEINLINE
ObpUnlockAllObjects (
    IN POBJECT_TYPE ObjectType
    )
{
    LONG i;

    for (i = OBJECT_LOCK_COUNT - 1; i >= 0; i--) {

        ExReleaseResourceLite(&(ObjectType->ObjectLocks[i]));       
    }

    KeLeaveCriticalRegion();                                                    
}

