/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    objmgr.c

Abstract:

    Object Manager object management routines for the NT Cluster Service

Author:

    Rod Gamache (rodga) 13-Mar-1996

Revision History:

--*/
#include "omp.h"

//
// Global data defined by this module
//

//
// The Object Type table and lock.
//
POM_OBJECT_TYPE OmpObjectTypeTable[ObjectTypeMax] = {0};
CRITICAL_SECTION OmpObjectTypeLock;

#if OM_TRACE_REF
LIST_ENTRY	gDeadListHead;
#endif
//
// Functions local to this module
//

#if OM_TRACE_OBJREF
DWORDLONG *OmpMatchRef = NULL;

VOID
OmpReferenceHeader(
    POM_HEADER pOmHeader
    )
{
    InterlockedIncrement(&(pOmHeader)->RefCount);
    if (&(pOmHeader)->Body == OmpMatchRef) {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[OM] Referencing %1!lx! - new ref %2!d!\n",
                   OmpMatchRef,
                   (pOmHeader)->RefCount);
    }
}

DWORD
OmpDereferenceHeader(
    IN POM_HEADER Header
    )
{
    if (&Header->Body == OmpMatchRef) {
        ClRtlLogPrint(LOG_CRITICAL,
                   "[OM] Dereferencing %1!lx! - old ref %2!d!\n",
                   OmpMatchRef,
                   Header->RefCount);
    }
    return(InterlockedDecrement(&Header->RefCount) == 0);
}
#endif



DWORD
WINAPI
OmCreateType(
    IN OBJECT_TYPE ObjectType,
    IN POM_OBJECT_TYPE_INITIALIZE ObjectTypeInitialize
    )

/*++

Routine Description:

    This routine creates an object of the type specified. This merely
    allocates an object type structure, and inserts a pointer to this
    structure into the OmpObjectTypeTable.

Arguments:
    ObjectType - The Object Type being created.
    ObjectTypeIntialize - The initialization info.

Returns:
    ERROR_SUCCESS if the request is successful.
    A Win32 error code on failure.

--*/

{
    POM_OBJECT_TYPE objType;
    DWORD objTypeSize;

    CL_ASSERT( ObjectType < ObjectTypeMax );
    CL_ASSERT( ARGUMENT_PRESENT(ObjectTypeInitialize) );
    CL_ASSERT( ObjectTypeInitialize->ObjectSize );

    //
    // Take out a lock, just in case there can be multiple threads.
    //

    EnterCriticalSection( &OmpObjectTypeLock );

    //
    // Check if this ObjectType is already allocated.
    //

    if ( OmpObjectTypeTable[ObjectType] != NULL ) {
        LeaveCriticalSection( &OmpObjectTypeLock );
        return(ERROR_OBJECT_ALREADY_EXISTS);
    }

    //
    // Allocate an object type block, plus its name.
    //

    objTypeSize = (sizeof(OM_OBJECT_TYPE) + sizeof(DWORDLONG)) &
                   ~sizeof(DWORDLONG);

    objType = LocalAlloc(LMEM_ZEROINIT, objTypeSize +
                          ((lstrlenW(ObjectTypeInitialize->Name) + 1) *
                            sizeof(WCHAR)));

    if ( objType == NULL ) {
        LeaveCriticalSection( &OmpObjectTypeLock );
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Init the object type block.
    //

    InitializeListHead(&objType->ListHead);
    InitializeListHead(&objType->CallbackListHead);
    InitializeCriticalSection(&objType->CriticalSection);

    objType->Type = ObjectType;

    objType->ObjectSize = ObjectTypeInitialize->ObjectSize;
    objType->Signature = ObjectTypeInitialize->Signature;
    objType->DeleteObjectMethod = ObjectTypeInitialize->DeleteObjectMethod;

    objType->Name = (LPWSTR)((PCHAR)objType + objTypeSize);
    lstrcpyW(objType->Name, ObjectTypeInitialize->Name);

    OmpObjectTypeTable[ObjectType] = objType;

    LeaveCriticalSection( &OmpObjectTypeLock );

    OmpLogPrint( L"OTCREATE \"%1!ws!\"\n", objType->Name );

    return(ERROR_SUCCESS);

} // OmCreateType



PVOID
WINAPI
OmCreateObject(
    IN OBJECT_TYPE ObjectType,
    IN LPCWSTR ObjectId,
    IN LPCWSTR ObjectName OPTIONAL,
    OUT PBOOL  Created OPTIONAL
    )

/*++

Routine Description:

    This routine creates an object of the type specified or opens an
    object if one of the same Id already exists. If the object is created
    its reference count is 1. If it is not create, then the reference count
    of the object is incremented.

Arguments:
    ObjectType - The type of object being created.
    ObjectId - The Id string for the object to find/create.
    ObjectName - The name to set for the object if found or created.
    Created - If present, returns TRUE if the object was created, returns
              FALSE otherwise.

Returns:
    A pointer to the created/opened object on success.
    A NULL on failure - use GetLastError to get the error code.

--*/

{
    DWORD status;
    PVOID object;
    PVOID tmpObject = NULL;
    LPWSTR objectName = NULL;
    POM_HEADER objHeader;
    POM_OBJECT_TYPE objType;
    DWORD objSize;

    CL_ASSERT( ObjectType < ObjectTypeMax );
    CL_ASSERT( OmpObjectTypeTable[ObjectType] );

    //
    // Get our Object Type block.
    //
    objType = OmpObjectTypeTable[ObjectType];

    //
    // Calculate size of this object (round it to a DWORDLONG).
    // Note: we don't subtract the DWORDLONG Body for rounding purposes.
    //
    objSize = (sizeof(OM_HEADER) + objType->ObjectSize) & ~sizeof(DWORDLONG);

    EnterCriticalSection( &objType->CriticalSection );

    //
    // Try to open the object first
    //
    object = OmReferenceObjectById( ObjectType, ObjectId );

    if ( object != NULL ) {
        status = ERROR_SUCCESS;
        if ( ARGUMENT_PRESENT(ObjectName) ) {
            //
            // Set the new ObjectName.
            //
            status = OmSetObjectName( object, ObjectName );

            //
            // If we failed, then return NULL.
            //
            if ( status != ERROR_SUCCESS ) {
				OmDereferenceObject( object );
				object = NULL;
            }
        }
        LeaveCriticalSection( &objType->CriticalSection );

        if ( ARGUMENT_PRESENT(Created) ) {
            *Created = FALSE;
        }

        SetLastError( status );
        return(object);
    }

    //
    // Attempt to allocate the object, plus its Id string.
    //
    objHeader = LocalAlloc(LMEM_ZEROINIT, objSize +
                           ((lstrlenW(ObjectId) + 1) * sizeof(WCHAR)));

    if ( objHeader == NULL ) {
        LeaveCriticalSection( &objType->CriticalSection );
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return(NULL);
    }

    if ( ARGUMENT_PRESENT(ObjectName) ) {
        //
        // Make sure ObjectName is unique.
        //
        tmpObject = OmReferenceObjectByName( ObjectType, ObjectName );
        if ( tmpObject != NULL ) {
            LeaveCriticalSection( &objType->CriticalSection );
            LocalFree( objHeader );
            SetLastError(ERROR_OBJECT_ALREADY_EXISTS);
            return(NULL);
        }

        objectName = LocalAlloc(LMEM_ZEROINIT,
                                (lstrlenW(ObjectName) + 1) * sizeof(WCHAR));

        if ( objectName == NULL ) {
            LeaveCriticalSection( &objType->CriticalSection );
            LocalFree( objHeader );
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return(NULL);
        }
        lstrcpyW( objectName, ObjectName );
    }

    //
    // Initialize the object.
    //
    InitializeListHead(&objHeader->ListEntry);
    objHeader->Signature = objType->Signature;
    objHeader->RefCount = 1;
    objHeader->ObjectType = objType;
    objHeader->Name = objectName;
    InitializeListHead(&objHeader->CbListHead);

    //
    // The Id string goes after the object header and body.
    //
    objHeader->Id = (LPWSTR)((PCHAR)objHeader + objSize);
    lstrcpyW(objHeader->Id, ObjectId);

    //
    // Tell the caller that we had to create this object.
    //
    if ( ARGUMENT_PRESENT(Created) ) {
        *Created = TRUE;
    }

#if  OM_TRACE_REF
	//SS: all objects are added to the dead list on creation
	// they are removed when the refcount goes to zero
    InitializeListHead(&objHeader->DeadListEntry);
    InsertTailList( &gDeadListHead, &objHeader->DeadListEntry );
#endif

    LeaveCriticalSection( &objType->CriticalSection );

    OmpLogPrint(L"OBCREATE \"%1!ws!\" \"%2!ws!\" \"%3!ws!\"\n",
                objType->Name,
                ObjectId,
                ObjectName == NULL ? L"" : ObjectName);

    return(&objHeader->Body);

} // OmCreateObject



DWORD
WINAPI
OmInsertObject(
    IN PVOID Object
    )

/*++

Routine Description:

    This routine inserts an object into the object's list.

Arguments:

    Object - A pointer to the object to be inserted into its object type list.

Returns:

    ERROR_SUCCESS - if the request was successful.
    ERROR_OBJECT_ALREADY_EXISTS if this object is already in the list.

--*/

{
    POM_HEADER objHeader;
    POM_HEADER otherHeader;
    POM_OBJECT_TYPE objType;

    //
    // Get our Object Header.
    //

    objHeader = OmpObjectToHeader( Object );

    //
    // Get our Object Type block.
    //

    objType = objHeader->ObjectType;

    //
    // Now perform the insertion, but first check to see if someone else
    // snuck in ahead of us and inserted another object of the same name.
    //

    EnterCriticalSection( &objType->CriticalSection );

    CL_ASSERT( !(objHeader->Flags & OM_FLAG_OBJECT_INSERTED) );

    otherHeader = OmpFindIdInList( &objType->ListHead, objHeader->Id );

    if ( otherHeader != NULL ) {
        // We loose!
        LeaveCriticalSection( &objType->CriticalSection );
        return(ERROR_OBJECT_ALREADY_EXISTS);
    }

    //
    // We generate the enumeration key for this object, and we must insert
    // the object at the tail of the list, so the list is ordered by EnumKey.
    // By definition, this entry must go at the end of the list.
    //

    objHeader->EnumKey = ++objType->EnumKey;
    CL_ASSERT( objHeader->EnumKey > 0 );

    InsertTailList( &objType->ListHead, &objHeader->ListEntry );

    objHeader->Flags |= OM_FLAG_OBJECT_INSERTED;

    LeaveCriticalSection( &objType->CriticalSection );

    return(ERROR_SUCCESS);

} // OmInsertObject



DWORD
WINAPI
OmRemoveObject(
    IN PVOID Object
    )

/*++

Routine Description:

    This routine removes an object from its object's list.

Arguments:

    Object - A pointer to the object to be removed from its object type list.

Returns:

    ERROR_SUCCESS if the request is successful.
    ERROR_RESOURCE_NOT_FOUND if the object is not in any list.

--*/

{
    POM_HEADER objHeader;
    POM_OBJECT_TYPE objType;

    //
    // Get our Object Header.
    //

    objHeader = OmpObjectToHeader( Object );

    //
    // Get our Object Type block.
    //

    objType = objHeader->ObjectType;

    //
    // Now perform the removal.
    //

    EnterCriticalSection( &objType->CriticalSection );

    if ( !(objHeader->Flags & OM_FLAG_OBJECT_INSERTED) ) {
        LeaveCriticalSection( &objType->CriticalSection );
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    RemoveEntryList( &objHeader->ListEntry );

    objHeader->Flags &= ~OM_FLAG_OBJECT_INSERTED;

    //
    // log while the lock is held so we don't lose our pointers
    //
    OmpLogPrint(L"OBDELETE \"%1!ws!\" \"%2!ws!\" \"%3!ws!\"\n",
                objType->Name,
                objHeader->Id,
                objHeader->Name == NULL ? L"" : objHeader->Name);

    LeaveCriticalSection( &objType->CriticalSection );

    return(ERROR_SUCCESS);

} // OmRemoveObject



PVOID
WINAPI
OmpReferenceObjectById(
    IN OBJECT_TYPE ObjectType,
    IN LPCWSTR Id
    )

/*++

Routine Description:

    This routine opens an object of the name and type specified. It also
    increments the reference count on the object.

Arguments:
    ObjectType - The Object Type to open.
    Id - The Id string of the object to open.

Returns:
    A pointer to the object on success.
    NULL on error.

--*/

{
    DWORD status;
    POM_OBJECT_TYPE objType;
    POM_HEADER objHeader;

    CL_ASSERT( ObjectType < ObjectTypeMax );
    CL_ASSERT( OmpObjectTypeTable[ObjectType] );

    //
    // Get our Object Type block.
    //

    objType = OmpObjectTypeTable[ObjectType];

    EnterCriticalSection( &objType->CriticalSection );

    //
    // Get the Object's header
    //
    objHeader = OmpFindIdInList( &objType->ListHead, Id );

    if ( objHeader == NULL ) {
        LeaveCriticalSection( &objType->CriticalSection );
        return(NULL);
    }

#if OM_TRACE_REF    
	OmReferenceObject(&objHeader->Body);
#else
    OmpReferenceHeader( objHeader );
#endif
    LeaveCriticalSection( &objType->CriticalSection );

    return(&objHeader->Body);

} // OmpReferenceObjectById



PVOID
WINAPI
OmpReferenceObjectByName(
    IN OBJECT_TYPE ObjectType,
    IN LPCWSTR Name
    )

/*++

Routine Description:

    This routine opens an object of the name and type specified. It also
    increments the reference count on the object.

Arguments:
    ObjectType - The Object Type to open.
    Name - The name of the object to open.

Returns:
    A pointer to the object on success.
    NULL on error.

--*/

{
    DWORD status;
    POM_OBJECT_TYPE objType;
    POM_HEADER objHeader;

    CL_ASSERT( ObjectType < ObjectTypeMax );
    CL_ASSERT( OmpObjectTypeTable[ObjectType] );

    //
    // Get our Object Type block.
    //

    objType = OmpObjectTypeTable[ObjectType];

    EnterCriticalSection( &objType->CriticalSection );

    //
    // Get the Object's header
    //

    objHeader = OmpFindNameInList( &objType->ListHead, Name );

    if ( objHeader == NULL ) {
        LeaveCriticalSection( &objType->CriticalSection );
        return(NULL);
    }

#if OM_TRACE_REF    
	OmReferenceObject(&objHeader->Body);
#else
    OmpReferenceHeader( objHeader );
#endif

    LeaveCriticalSection( &objType->CriticalSection );

    return(&objHeader->Body);

} // OmReferenceObjectByName


DWORD
WINAPI
OmCountObjects(
    IN OBJECT_TYPE ObjectType,
    OUT LPDWORD NumberOfObjects
    )

/*++

Routine Description:

    Returns the count of the number of objects of a particular type
    which exist in the database at this time.

Arguments:

    ObjectType - The object type to count.

    NumberOfObjects - On output, contains the number of objects of the
                      specified type in the database.

Return Value:

    ERROR_SUCCESS - if the request is successful.
    A Win32 error if the request fails.

--*/

{
    POM_OBJECT_TYPE objType;
    PLIST_ENTRY listEntry;
    DWORD objectCount = 0;


    CL_ASSERT( ObjectType < ObjectTypeMax );

    objType = OmpObjectTypeTable[ObjectType];

    if ( !objType ) {
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    EnterCriticalSection(&objType->CriticalSection);


    for ( listEntry = objType->ListHead.Flink;
          listEntry != &(objType->ListHead);
          listEntry = listEntry->Flink
        )
    {
        objectCount++;
    }

    LeaveCriticalSection(&objType->CriticalSection);

    *NumberOfObjects = objectCount;

    return(ERROR_SUCCESS);

} // OmCountObjects



DWORD
WINAPI
OmEnumObjects(
    IN OBJECT_TYPE ObjectType,
    IN OM_ENUM_OBJECT_ROUTINE EnumerationRoutine,
    IN PVOID Context1,
    IN PVOID Context2
    )

/*++

Routine Description:

    Enumerates all objects of the specified type.

Arguments:

    ObjectType - The object type to enumerate.

    EnumerationRoutine - Supplies the enumeration routine to be
        called for each object.

    Context1 - Supplies a context pointer to be passed to the
        enumeration routine.

    Context2 - Supplies a second context pointer to be passed to the
        enumeration routine.

Return Value:

    ERROR_SUCCESS - if the request is successful.
    A Win32 error if the request fails.

--*/

{
    POM_OBJECT_TYPE objType;
    POM_HEADER objHeader;
    PLIST_ENTRY listEntry;
    DWORD   enumKey = 0;

    CL_ASSERT( ObjectType < ObjectTypeMax );

    objType = OmpObjectTypeTable[ObjectType];

    if ( !objType ) {
        return(ERROR_RESOURCE_NOT_FOUND);
    }

    //
    // Enumeration is a little tricky. First, we have to allow for multiple
    // entries in the enumeration list to be removed as a side effect of the
    // callout. Second, we have to allow the list lock to be released so the
    // first issue can be handled. We'll use a sort order key to remember where
    // we are in the enumeration and pick up from the next highest value.
    //

    while ( TRUE ) {

        EnterCriticalSection(&objType->CriticalSection);

        //
        // Skip to next entry to process in list.
        // We can treat this like an entry only after verifying it is not the
        // ListHeader.
        //

        listEntry = objType->ListHead.Flink;
        objHeader = CONTAINING_RECORD( listEntry, OM_HEADER, ListEntry );

        while ( listEntry != &objType->ListHead &&
                objHeader->EnumKey <= enumKey ) {
            listEntry = listEntry->Flink;
            objHeader = CONTAINING_RECORD( listEntry, OM_HEADER, ListEntry );
        }

        //
        // Save the enumeration key for next iteration.
        //

        enumKey = objHeader->EnumKey;

        //if it is a valid object, increment the reference count
        // so that it is not deleted while the call out is being
        // made
        if ( listEntry != &objType->ListHead ) {
            OmReferenceObject(&objHeader->Body);
        }
        //
        // Drop the lock to return or call out.
        //

        LeaveCriticalSection(&objType->CriticalSection);

        if ( listEntry == &objType->ListHead ) {
            return(ERROR_SUCCESS);
        }

        if (!(EnumerationRoutine)(Context1,
                                  Context2,
                                  &objHeader->Body,
                                  objHeader->Id)) {
            OmDereferenceObject(&objHeader->Body);
            break;
        }
        OmDereferenceObject(&objHeader->Body);
    }

    return(ERROR_SUCCESS);

} // OmEnumObject



VOID
OmpDereferenceObject(
    IN PVOID Object
    )

/*++

Routine Description:

    This routine dereferences an object. If the reference count goes to
    zero, then the object is deallocated.

Arguments:
    Object - A pointer to the object to be dereferenced.

Returns:
    None

--*/

{
    DWORD status;
    POM_HEADER objHeader;
    POM_OBJECT_TYPE objType;

    objHeader = OmpObjectToHeader( Object );

    objType = objHeader->ObjectType;

    CL_ASSERT( objHeader->RefCount != 0xfeeefeee );
    CL_ASSERT( objHeader->RefCount > 0 );

    if ( OmpDereferenceHeader(objHeader) ) {

        //
        // The reference count has gone to zero. Acquire the
        // lock, remove the object from the list, and perform
        // cleanup.
        //

        EnterCriticalSection( &objType->CriticalSection );

        //
        // Check the ref count again, to close the race condition between
        // open/create and this routine.
        //

        if ( objHeader->RefCount == 0 ) {
            //
            // If the object hasn't been previously removed from it's
            // object type list, then remove it now.
            //

            if ( objHeader->Flags & OM_FLAG_OBJECT_INSERTED ) {
                RemoveEntryList( &objHeader->ListEntry );
                objHeader->Flags &= ~OM_FLAG_OBJECT_INSERTED;
            }

            //
            // Call the object type's delete method (if present).
            //

            if ( ARGUMENT_PRESENT( objType->DeleteObjectMethod ) ) {
                (objType->DeleteObjectMethod)( &objHeader->Body );
            }

            objHeader->Signature = 'rFmO';
#if OM_TRACE_REF
			RemoveEntryList(&objHeader->DeadListEntry);
#endif			
            if ( objHeader->Name != NULL ) {
                ClRtlLogPrint(LOG_NOISE,
                           "[OM] Deleting object %1!ws! (%2!ws!)\n",
                           objHeader->Name,
                           objHeader->Id);
                LocalFree( objHeader->Name );
            } else {
                ClRtlLogPrint(LOG_NOISE,
                           "[OM] Deleting object %1!ws!\n",
                           objHeader->Id);
            }
            LocalFree( objHeader );
        }
        LeaveCriticalSection( &objType->CriticalSection );
    }

} // OmpDereferenceObject



DWORD
WINAPI
OmSetObjectName(
    IN PVOID    Object,
    IN LPCWSTR  ObjectName
    )

/*++

Routine Description:

    Set the object name for an object. If the ObjectName already exists on a
    different object, then this call will fail.

Arguments:

    Object - A pointer to the object to set its name.
    ObjectName - The name to set for the object.

Return Value:

    ERROR_SUCCESS if successful.
    A Win32 error code on failure.

--*/

{
    DWORD status = ERROR_SUCCESS;
    PVOID object = NULL;
    LPWSTR objectName;
    POM_HEADER objHeader;
    POM_OBJECT_TYPE objType;

    //
    // Make sure object name is valid (not empty)
    //
    if (ObjectName[0] == '\0') 
    {
        status = ERROR_INVALID_NAME;
        goto FnExit;
    }

    objHeader = OmpObjectToHeader( Object );

    objType = objHeader->ObjectType;

    EnterCriticalSection( &objType->CriticalSection );

    //
    // Make sure ObjectName is unique.
    //
    object = OmReferenceObjectByName( objType->Type, ObjectName );
    if ( object != NULL ) 
    {
        //
        // If our's is the other object, then nothing to do. Otherwise,
        // there is a duplicate.
        //
        if ( object != Object ) 
        {
            status = ERROR_OBJECT_ALREADY_EXISTS;
            goto FnUnlock;
        }
    } 
    else 
    {
        //
        // No other object with the new name, then set the new name.
        //
        objectName = LocalAlloc(LMEM_ZEROINIT,
                                (lstrlenW(ObjectName) + 1) * sizeof(WCHAR));
        if ( objectName == NULL ) {
            status = ERROR_NOT_ENOUGH_MEMORY;
        } else {
            if ( objHeader->Name != NULL ) {
                LocalFree( objHeader->Name );
            }
            objHeader->Name = objectName;
            lstrcpyW( objectName, ObjectName );

            OmpLogPrint(L"OBRENAME \"%1!ws!\" \"%2!ws!\" \"%3!ws!\"\n",
                        objType->Name,
                        objHeader->Id,
                        ObjectName);
        }
    }

FnUnlock:
    LeaveCriticalSection( &objType->CriticalSection );
FnExit:
	if (object)
	{
		OmDereferenceObject(object);
	}    	
    return(status);

} // OmSetObjectName



DWORD
WINAPI
OmRegisterTypeNotify(
    IN OBJECT_TYPE          ObjectType,
    IN PVOID                pContext,
    IN DWORD                dwNotifyMask,
    IN OM_OBJECT_NOTIFYCB   pfnObjNotifyCb
    )

/*++

Routine Description:

    Registers a callback to be called by the FM on object state changes.

Arguments:

    ObjectType - The object type that notifications should be delivered for.

    pContext - A pointer to context information that is passed back into the callback.

    dwNotifyMask - The type of notifications that should be delivered

    pfnObjNotifyCb - a pointer to the callback.

Return Value:

    ERROR_SUCCESS if successful.
    A Win32 error code on failure.

--*/

{
    DWORD               dwError = ERROR_SUCCESS;
    POM_HEADER          pObjHeader;
    POM_OBJECT_TYPE     pObjType;
    POM_NOTIFY_RECORD   pNotifyRec;

    if ( !pfnObjNotifyCb ) {
        return(ERROR_INVALID_PARAMETER);
    }

    pObjType = OmpObjectTypeTable[ObjectType];

    //
    // The object type lock is used to serialize callbacks. This
    // is so that callees do not deadlock if they are waiting on
    // another thread that needs to enumerate objects.
    //
    EnterCriticalSection( &OmpObjectTypeLock );

    //
    // First, check if the same notification is being registered twice!
    // If so, then just change the notification mask and context.
    //
    pNotifyRec = OmpFindNotifyCbInList( &pObjType->CallbackListHead,
                                        pfnObjNotifyCb);
    if ( !pNotifyRec ) {
        pNotifyRec = (POM_NOTIFY_RECORD) LocalAlloc(LMEM_FIXED,sizeof(OM_NOTIFY_RECORD));

        if ( !pNotifyRec ) {
            dwError = ERROR_NOT_ENOUGH_MEMORY;
            CsInconsistencyHalt(dwError);
            goto FnExit;
        }

        pNotifyRec->pfnObjNotifyCb = pfnObjNotifyCb;

        //insert the notification record at the tail
        InsertTailList(&pObjType->CallbackListHead, &pNotifyRec->ListEntry);
    }

    pNotifyRec->dwNotifyMask = dwNotifyMask;
    pNotifyRec->pContext = pContext;

FnExit:
    LeaveCriticalSection( &OmpObjectTypeLock );

    return(dwError);

} // OmRegisterTypeNotify



DWORD
WINAPI
OmRegisterNotify(
    IN PVOID                pObject,
    IN PVOID                pContext,
    IN DWORD                dwNotifyMask,
    IN OM_OBJECT_NOTIFYCB   pfnObjNotifyCb
    )

/*++

Routine Description:

    Registers a callback to be called by the FM on object state changes.

Arguments:

    pObject - A pointer to the object to set its name.
    pContext - A pointer to context information that is passed back into the callback.
    dwNotifyMask - The name to set for the object.
    pfnObjNotifyCb - a pointer to the callback.

Return Value:

    ERROR_SUCCESS if successful.
    A Win32 error code on failure.

--*/

{
    DWORD               dwError = ERROR_SUCCESS;
    POM_HEADER          pObjHeader;
    POM_OBJECT_TYPE     pObjType;
    POM_NOTIFY_RECORD   pNotifyRec;

    if ( !pfnObjNotifyCb ) {
        return(ERROR_INVALID_PARAMETER);
    }

    pObjHeader = OmpObjectToHeader( pObject );

    pObjType = pObjHeader->ObjectType;

    EnterCriticalSection( &OmpObjectTypeLock );

    //
    // First, check if the same notification is being registered twice!
    // If so, then just change the notification mask and context.
    //
    pNotifyRec = OmpFindNotifyCbInList(&pObjHeader->CbListHead, pfnObjNotifyCb);
    if ( !pNotifyRec ) {
        pNotifyRec = (POM_NOTIFY_RECORD) LocalAlloc(LMEM_FIXED,sizeof(OM_NOTIFY_RECORD));

        if ( !pNotifyRec ) {
            dwError = ERROR_NOT_ENOUGH_MEMORY;
            CsInconsistencyHalt(dwError);
            goto FnExit;
        }

        pNotifyRec->pfnObjNotifyCb = pfnObjNotifyCb;

        //insert the notification record at the tail
        InsertTailList(&pObjHeader->CbListHead, &pNotifyRec->ListEntry);
    }

    pNotifyRec->dwNotifyMask = dwNotifyMask;
    pNotifyRec->pContext = pContext;

FnExit:
    LeaveCriticalSection( &OmpObjectTypeLock );

    return(dwError);

} // OmRegisterNotify


DWORD
WINAPI
OmDeregisterNotify(
    IN PVOID                    pObject,
    IN OM_OBJECT_NOTIFYCB       pfnObjNotifyCb
    )

/*++

Routine Description:

    Removes the callback registed with the object.

Arguments:

    pObject - A pointer to the object to set its name.
    pfnObjNotifyCb - a pointer to the callback.

Return Value:

    ERROR_SUCCESS if successful.
    A Win32 error code on failure.

--*/

{
    DWORD               dwError = ERROR_SUCCESS;
    POM_HEADER          pObjHeader;
    POM_OBJECT_TYPE     pObjType;
    POM_NOTIFY_RECORD   pNotifyRec;

    if ( !pfnObjNotifyCb ) {
        return(ERROR_INVALID_PARAMETER);
    }


    pObjHeader = OmpObjectToHeader( pObject );


        //SS: we use the same crit section for list manipulations
    pObjType = pObjHeader->ObjectType;

    //
    // The object type lock is used to serialize callbacks. This
    // is so that callees do not deadlock if they are waiting on
    // another thread that needs to enumerate objects.
    //
    EnterCriticalSection( &OmpObjectTypeLock );

    pNotifyRec = OmpFindNotifyCbInList(&pObjHeader->CbListHead, pfnObjNotifyCb);
    if (!pNotifyRec) {
            ClRtlLogPrint(LOG_UNUSUAL,
                    "[OM] OmRegisterNotify: OmpFindNotifyCbInList failed for 0x%1!08lx!\r\n",
                    pfnObjNotifyCb);

            dwError = ERROR_INVALID_PARAMETER;
            CL_LOGFAILURE(dwError);
            goto FnExit;
    }
    RemoveEntryList(&pNotifyRec->ListEntry);

FnExit:
    LeaveCriticalSection( &OmpObjectTypeLock );

    return(dwError);

} // OmRegisterNotify



DWORD
WINAPI
OmNotifyCb(
    IN PVOID pObject,
    IN DWORD dwNotification
    )
/*++

Routine Description:

    The callback registered with the quorum resource object.

Arguments:

    pContext - The resource whose call back list will be traversed.
        dwNotification - The notification to be passed to the callback.

Return Value:

    ERROR_SUCCESS if successful.

    A Win32 error code on failure.

--*/

{
    POM_HEADER              pObjHeader;
    POM_OBJECT_TYPE         pObjType;
    PLIST_ENTRY             ListEntry;
    DWORD                   dwError=ERROR_SUCCESS;
    POM_NOTIFY_RECORD       pNotifyRecList = NULL;
    DWORD                   dwCount;
    DWORD                   i;
    
    CL_ASSERT(pObject);

    //get the callback list
    pObjHeader = OmpObjectToHeader(pObject);
    pObjType = pObjHeader->ObjectType;

    //will walk the list of callbacks, do allow more registrations
    EnterCriticalSection(&OmpObjectTypeLock);
    dwError = OmpGetCbList(pObject, &pNotifyRecList, &dwCount);
    LeaveCriticalSection(&OmpObjectTypeLock);

    for (i=0; i < dwCount; i++)
    {
        if (pNotifyRecList[i].dwNotifyMask & dwNotification) {
            (pNotifyRecList[i].pfnObjNotifyCb)(pNotifyRecList[i].pContext,
                                         pObject,
                                         dwNotification);
        }
    }

    LocalFree(pNotifyRecList);
    return(dwError);
}    

DWORD OmpGetCbList(
    IN PVOID                pObject,
    OUT POM_NOTIFY_RECORD   *ppNotifyRecList,
    OUT LPDWORD             pdwCount
)    
{
    DWORD                   status = ERROR_SUCCESS;
    POM_NOTIFY_RECORD       pNotifyRecList;
    POM_NOTIFY_RECORD       pNotifyRec;
    DWORD                   dwAllocated;
    PLIST_ENTRY             ListEntry;
    DWORD                   dwRetrySize=1;
    POM_HEADER              pObjHeader;
    POM_OBJECT_TYPE         pObjType;
    DWORD                   i = 0;

    *ppNotifyRecList = NULL;
    *pdwCount = 0;
Retry:    
    dwAllocated = ENUM_GROW_SIZE * dwRetrySize;

    pObjHeader = OmpObjectToHeader(pObject);
    pObjType = pObjHeader->ObjectType;

    pNotifyRecList = LocalAlloc(LMEM_FIXED, sizeof(OM_NOTIFY_RECORD) * ENUM_GROW_SIZE);
    if ( pNotifyRecList == NULL ) {
        status = ERROR_NOT_ENOUGH_MEMORY;
        goto FnExit;
    }

    ZeroMemory( pNotifyRecList, sizeof(OM_NOTIFY_RECORD) * ENUM_GROW_SIZE );


    //
    // First notify any type-specific callbacks
    //
    ListEntry = pObjType->CallbackListHead.Flink;
    while (ListEntry != &pObjType->CallbackListHead) {
        pNotifyRec = CONTAINING_RECORD(ListEntry,
                                       OM_NOTIFY_RECORD,
                                       ListEntry);
        if (i < dwAllocated)
        {
            CopyMemory(&pNotifyRecList[i++], pNotifyRec, sizeof(OM_NOTIFY_RECORD));
        }
        else
        {
            LocalFree(pNotifyRecList);
            dwRetrySize++;
            goto Retry;
        }
        ListEntry = ListEntry->Flink;
    }

    //
    // Next notify any resource-specific callbacks
    //
    ListEntry = pObjHeader->CbListHead.Flink;
    while (ListEntry != &(pObjHeader->CbListHead)) {
        pNotifyRec = CONTAINING_RECORD(ListEntry, OM_NOTIFY_RECORD, ListEntry);

        if (i < dwAllocated)
        {
            CopyMemory(&pNotifyRecList[i++], pNotifyRec, sizeof(OM_NOTIFY_RECORD));
        }
        else
        {
            LocalFree(pNotifyRecList);
            dwRetrySize++;
            goto Retry;
        }
        ListEntry = ListEntry->Flink;

    }


FnExit:
    *ppNotifyRecList = pNotifyRecList;
    *pdwCount = i;
    return(status);

}