//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
//

/*++



Module Name:

    shmobjectmgr.cpp

Abstract:
    Shared memory based object manager



--*/

#include "shmobjectmanager.hpp"
#include "shmobject.hpp"
#include "pal/cs.hpp"
#include "pal/thread.hpp"
#include "pal/procobj.hpp"
#include "pal/dbgmsg.h"

SET_DEFAULT_DEBUG_CHANNEL(PAL);

#include "pal/corunix.inl"

using namespace CorUnix;

IPalObjectManager * CorUnix::g_pObjectManager;

static
PAL_ERROR
CheckObjectTypeAndRights(
    IPalObject *pobj,
    CAllowedObjectTypes *paot,
    DWORD dwRightsGranted,
    DWORD dwRightsRequired
    );

/*++
Function:
  CSharedMemoryObjectManager::Initialize

  Performs (possibly failing) startup tasks for the object manager

Parameters:
  None
--*/

PAL_ERROR
CSharedMemoryObjectManager::Initialize(
    void
    )
{
    PAL_ERROR palError = NO_ERROR;

    ENTRY("CSharedMemoryObjectManager::Initialize (this=%p)\n", this);
    
    InitializeListHead(&m_leNamedObjects);
    InitializeListHead(&m_leAnonymousObjects);

    InternalInitializeCriticalSection(&m_csListLock);
    m_fListLockInitialized = TRUE;

    palError = m_HandleManager.Initialize();

    LOGEXIT("CSharedMemoryObjectManager::Initialize returns %d", palError);
    
    return palError;
}

/*++
Function:
  CSharedMemoryObjectManager::Shutdown

  Cleans up the object manager. This routine will call cleanup routines
  for all objects referenced by this process. After this routine is called
  no attempt should be made to access an IPalObject.

Parameters:
  pthr -- thread data for calling thread
--*/

PAL_ERROR
CSharedMemoryObjectManager::Shutdown(
    CPalThread *pthr
    )
{
    PLIST_ENTRY ple;
    CSharedMemoryObject *pshmobj;

    _ASSERTE(NULL != pthr);
    
    ENTRY("CSharedMemoryObjectManager::Shutdown (this=%p, pthr=%p)\n",
        this,
        pthr
        );

    InternalEnterCriticalSection(pthr, &m_csListLock);
    SHMLock();

    while (!IsListEmpty(&m_leAnonymousObjects))
    {
        ple = RemoveTailList(&m_leAnonymousObjects);
        pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);
        pshmobj->CleanupForProcessShutdown(pthr);
    }

    while (!IsListEmpty(&m_leNamedObjects))
    {
        ple = RemoveTailList(&m_leNamedObjects);
        pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);
        pshmobj->CleanupForProcessShutdown(pthr);
    }

    SHMRelease();
    InternalLeaveCriticalSection(pthr, &m_csListLock);

    LOGEXIT("CSharedMemoryObjectManager::Shutdown returns %d\n", NO_ERROR);
    
    return NO_ERROR;
}

/*++
Function:
  CSharedMemoryObjectManager::AllocateObject

  Allocates a new object instance of the specified type.

Parameters:
  pthr -- thread data for calling thread
  pot -- type of object to allocate
  poa -- attributes (name and SD) of object to allocate
  ppobjNew -- on success, receives a reference to the new object
--*/

PAL_ERROR
CSharedMemoryObjectManager::AllocateObject(
    CPalThread *pthr,
    CObjectType *pot,
    CObjectAttributes *poa,
    IPalObject **ppobjNew            // OUT
    )
{
    PAL_ERROR palError = NO_ERROR;
    CSharedMemoryObject *pshmobj = NULL;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != pot);
    _ASSERTE(NULL != poa);
    _ASSERTE(NULL != ppobjNew);

    ENTRY("CSharedMemoryObjectManager::AllocateObject "
        "(this=%p, pthr=%p, pot=%p, poa=%p, ppobjNew=%p)\n",
        this,
        pthr,
        pot,
        poa,
        ppobjNew
        );

    if (CObjectType::WaitableObject == pot->GetSynchronizationSupport())
    {
        pshmobj = InternalNew<CSharedMemoryWaitableObject>(pot, &m_csListLock);
    }
    else
    {
        pshmobj = InternalNew<CSharedMemoryObject>(pot, &m_csListLock);
    }

    if (NULL != pshmobj)
    {
        palError = pshmobj->Initialize(pthr, poa);
        if (NO_ERROR == palError)
        {
            *ppobjNew = static_cast<IPalObject*>(pshmobj);
        }
    }
    else
    {
        ERROR("Unable to allocate pshmobj\n");
        palError = ERROR_OUTOFMEMORY;
    }

    LOGEXIT("CSharedMemoryObjectManager::AllocateObject returns %d\n", palError);
    return palError;
}

/*++
Function:
  CSharedMemoryObjectManager::RegisterObject

  Registers a newly-allocated object instance. If the object to be registered
  has a name, and a previously registered object has the same name the new
  object will not be registered.

Distinguished return values:
  ERROR_ALREADY_EXISTS -- an object of a compatible type was already registered
    with the specified name
  ERROR_INVALID_HANDLE -- an object of an incompatible type was already
    registered with the specified name

Parameters:
  pthr -- thread data for calling thread
  pobjToRegister -- the object instance to register. This routine will always
    call ReleaseReference on this instance
  paot -- object types that are compatible with the new object instance
  dwRightsRequested -- requested access rights for the returned handle (ignored)
  pHandle -- on success, receives a handle to the registered object
  ppobjRegistered -- on success, receives a reference to the registered object
    instance.
--*/

PAL_ERROR
CSharedMemoryObjectManager::RegisterObject(
    CPalThread *pthr,
    IPalObject *pobjToRegister,
    CAllowedObjectTypes *paot,
    DWORD dwRightsRequested,
    HANDLE *pHandle,                 // OUT
    IPalObject **ppobjRegistered     // OUT
    )
{
    PAL_ERROR palError = NO_ERROR;
    CSharedMemoryObject *pshmobj = static_cast<CSharedMemoryObject*>(pobjToRegister);
    SHMObjData *psmodNew = NULL;
    CObjectAttributes *poa;
    CObjectType *potObj;
    IPalObject *pobjExisting;
    BOOL fInherit = FALSE;
    BOOL fShared = FALSE;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != pobjToRegister);
    _ASSERTE(NULL != paot);
    _ASSERTE(NULL != pHandle);
    _ASSERTE(NULL != ppobjRegistered);

    ENTRY("CSharedMemoryObjectManager::RegisterObject "
        "(this=%p, pthr=%p, pobjToRegister=%p, paot=%p, "
        "dwRightsRequested=%d, pHandle=%p, ppobjRegistered=%p)\n",
        this,
        pthr,
        pobjToRegister,
        paot,
        dwRightsRequested,
        pHandle,
        ppobjRegistered
        );

    poa = pobjToRegister->GetObjectAttributes();
    _ASSERTE(NULL != poa);

    if (NULL != poa->pSecurityAttributes)
    {
        fInherit = poa->pSecurityAttributes->bInheritHandle;
    }

    potObj = pobjToRegister->GetObjectType();
    fShared = (SharedObject == pshmobj->GetObjectDomain());
    
    InternalEnterCriticalSection(pthr, &m_csListLock);

    if (fShared)
    {
        //
        // We only need to acquire the shared memory lock if this
        // object is actually shared.
        //
        
        SHMLock();
    }

    if (0 != poa->sObjectName.GetStringLength())
    {
        SHMPTR shmObjectListHead = SHMNULL;

        //
        // The object must be shared
        //

        _ASSERTE(fShared);
        
        //
        // Check if an object by this name alredy exists
        //

        palError = LocateObject(
            pthr,
            &poa->sObjectName,
            paot,
            &pobjExisting
            );

        if (NO_ERROR == palError)
        {
            //
            // Obtain a new handle to the existing object
            //

            palError = ObtainHandleForObject(
                pthr,
                pobjExisting,
                dwRightsRequested,
                fInherit,
                NULL, 
                pHandle
                );

            if (NO_ERROR == palError)
            {
                //
                // Transfer object reference to out param
                //

                *ppobjRegistered = pobjExisting;
                palError = ERROR_ALREADY_EXISTS;
            }
            else
            {
                pobjExisting->ReleaseReference(pthr);
            }

            goto RegisterObjectExit;
        }
        else if (ERROR_INVALID_NAME != palError)
        {
            //
            // Something different than an object not found error
            // occurred. This is most likely due to a type conflict.
            //

            goto RegisterObjectExit;
        }

        //
        // Insert the object on the named object lists
        //

        InsertTailList(&m_leNamedObjects, pshmobj->GetObjectListLink());

        psmodNew = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
        if (NULL == psmodNew)
        {
            ASSERT("Failure to map shared object data\n");
            palError = ERROR_INTERNAL_ERROR;
            goto RegisterObjectExit;
        }

        shmObjectListHead = SHMGetInfo(SIID_NAMED_OBJECTS);
        if (SHMNULL != shmObjectListHead)
        {
            SHMObjData *psmodListHead;
            
            psmodListHead = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjectListHead);
            if (NULL != psmodListHead)
            {
                psmodNew->shmNextObj = shmObjectListHead;
                psmodListHead->shmPrevObj = pshmobj->GetShmObjData();
            }
            else
            {
                ASSERT("Failure to map shared object data\n");
                palError = ERROR_INTERNAL_ERROR;
                goto RegisterObjectExit;
            }
        }

        psmodNew->fAddedToList = TRUE;

        if (!SHMSetInfo(SIID_NAMED_OBJECTS, pshmobj->GetShmObjData()))
        {
            ASSERT("Failed to set shared named object list head\n");
            palError = ERROR_INTERNAL_ERROR;
            goto RegisterObjectExit;
        }
    }
    else
    {
        //
        // Place the object on the anonymous object list
        //

        InsertTailList(&m_leAnonymousObjects, pshmobj->GetObjectListLink());
    }

    //
    // Hoist the object's immutable data (if any) into shared memory if
    // the object is shared
    //

    if (fShared && 0 != potObj->GetImmutableDataSize())
    {
        VOID *pvImmutableData;
        SHMObjData *psmod;

        palError = pobjToRegister->GetImmutableData(&pvImmutableData);
        if (NO_ERROR != palError)
        {
            ASSERT("Failure to obtain object immutable data\n");
            goto RegisterObjectExit;
        }

        psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
        if (NULL != psmod)
        {
            VOID *pvSharedImmutableData =
                SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjImmutableData);
            
            if (NULL != pvSharedImmutableData)
            {
                CopyMemory(
                    pvSharedImmutableData,
                    pvImmutableData,
                    potObj->GetImmutableDataSize()
                    );
            }
            else
            {
                ASSERT("Failure to map psmod->shmObjImmutableData\n");
                palError = ERROR_INTERNAL_ERROR;
                goto RegisterObjectExit;
            }
        }
        else
        {
            ASSERT("Failure to map pshmobj->GetShmObjData()\n");
            palError = ERROR_INTERNAL_ERROR;
            goto RegisterObjectExit;
        }
    }

    //
    // Obtain a handle for the new object
    //

    palError = ObtainHandleForObject(
        pthr,
        pobjToRegister,
        dwRightsRequested,
        fInherit,
        NULL, 
        pHandle
        );

    if (NO_ERROR == palError)
    {
        //
        // Transfer pobjToRegister reference to out param
        //

        *ppobjRegistered = pobjToRegister;
        pobjToRegister = NULL;
    }
        
RegisterObjectExit:

    if (fShared)
    {
        SHMRelease();
    }
    
    InternalLeaveCriticalSection(pthr, &m_csListLock);

    if (NULL != pobjToRegister)
    {
        pobjToRegister->ReleaseReference(pthr);
    }

    LOGEXIT("CSharedMemoryObjectManager::RegisterObject return %d\n", palError);

    return palError;
}

/*++
Function:
  CSharedMemoryObjectManager::LocateObject

  Search for a previously registered object with a give name and type

Distinguished return values:
  ERROR_INVALID_NAME -- no object with the specified name was previously
    registered
  ERROR_INVALID_HANDLE -- an object with the specified name was previously
    registered, but its type is not compatible

Parameters:
  pthr -- thread data for calling thread
  psObjectToLocate -- the name of the object to locate
  paot -- acceptable types for the object
  ppobj -- on success, receives a reference to the object instance
--*/

PAL_ERROR
CSharedMemoryObjectManager::LocateObject(
    CPalThread *pthr,
    CPalString *psObjectToLocate,
    CAllowedObjectTypes *paot,
    IPalObject **ppobj               // OUT
    )
{
    PAL_ERROR palError = NO_ERROR;
    IPalObject *pobjExisting = NULL;
    SHMPTR shmSharedObjectData = SHMNULL;
    SHMPTR shmObjectListEntry = SHMNULL;
    SHMObjData *psmod = NULL;
    LPWSTR pwsz = NULL;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != psObjectToLocate);
    _ASSERTE(NULL != psObjectToLocate->GetString());
    _ASSERTE(PAL_wcslen(psObjectToLocate->GetString()) == psObjectToLocate->GetStringLength());
    _ASSERTE(NULL != ppobj);

    ENTRY("CSharedMemoryObjectManager::LocateObject "
        "(this=%p, pthr=%p, psObjectToLocate=%p, paot=%p, "
        "ppobj=%p)\n",
        this,
        pthr,
        psObjectToLocate,
        paot,
        ppobj
        );

    TRACE("Searching for object name %S\n", psObjectToLocate->GetString());

    InternalEnterCriticalSection(pthr, &m_csListLock);

    //
    // Search the local named object list for this object
    //

    for (PLIST_ENTRY ple = m_leNamedObjects.Flink;
         ple != &m_leNamedObjects;
         ple = ple->Flink)
    {
        CObjectAttributes *poa;
        CSharedMemoryObject *pshmobj =
            CSharedMemoryObject::GetObjectFromListLink(ple);

        poa = pshmobj->GetObjectAttributes();
        _ASSERTE(NULL != poa);

        if (poa->sObjectName.GetStringLength() != psObjectToLocate->GetStringLength())
        {
            continue;
        }

        if (0 != PAL_wcscmp(poa->sObjectName.GetString(), psObjectToLocate->GetString()))
        {
            continue;
        }

        //
        // This object has the name we're looking for
        //

        pobjExisting = static_cast<IPalObject*>(pshmobj);
        break;        
    } 

    if (NULL != pobjExisting)
    {
        //
        //  Validate the located object's type
        //

        if (paot->IsTypeAllowed(
                pobjExisting->GetObjectType()->GetId()
                ))
        {
            TRACE("Local object exists with compatible type\n");
            
            //
            // Add a reference to the found object
            //

            pobjExisting->AddReference();
            *ppobj = pobjExisting;
        }
        else
        {
            TRACE("Local object exists w/ incompatible type\n");            
            palError = ERROR_INVALID_HANDLE;
        }
        
        goto LocateObjectExit;
    }

    //
    // Search the shared memory named object list for a matching object
    //
    
    SHMLock();
    
    shmObjectListEntry = SHMGetInfo(SIID_NAMED_OBJECTS);
    while (SHMNULL != shmObjectListEntry)
    {
        psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjectListEntry);
        if (NULL != psmod)
        {
            if (psmod->dwNameLength == psObjectToLocate->GetStringLength())
            {
                pwsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName);
                if (NULL != pwsz)
                {
                    if (0 == PAL_wcscmp(pwsz, psObjectToLocate->GetString()))
                    {
                        //
                        // This is the object we were looking for.
                        //

                        shmSharedObjectData = shmObjectListEntry;
                        break;
                    }
                }
                else
                {
                    ASSERT("Unable to map psmod->shmObjName\n");
                    break;
                }                
            }

            shmObjectListEntry = psmod->shmNextObj;
        }
        else
        {
            ASSERT("Unable to map shmObjectListEntry\n");
            break;
        }
    }

    if (SHMNULL != shmSharedObjectData)
    {
        CSharedMemoryObject *pshmobj = NULL;
        CObjectAttributes oa(pwsz, NULL);

        //
        // Check if the type is allowed
        //

        if (!paot->IsTypeAllowed(psmod->eTypeId))
        {
            TRACE("Remote object exists w/ incompatible type\n");
            palError = ERROR_INVALID_HANDLE;
            goto LocateObjectExitSHMRelease;
        }
        
        //
        // Get the local instance of the CObjectType
        //
        
        CObjectType *pot = CObjectType::GetObjectTypeById(psmod->eTypeId);
        if (NULL == pot)
        {
            ASSERT("Invalid object type ID in shared memory info\n");
            goto LocateObjectExitSHMRelease;
        }

        TRACE("Remote object exists compatible type -- importing\n");
        
        //
        // Create the local state for the shared object
        //

        palError = ImportSharedObjectIntoProcess(
            pthr,
            pot,
            &oa,
            shmSharedObjectData,
            psmod,
            TRUE,
            &pshmobj
            );

        if (NO_ERROR == palError)
        {   
            *ppobj = static_cast<IPalObject*>(pshmobj);
        }
        else
        {
            ERROR("Failure initializing object from shared data\n");
            goto LocateObjectExitSHMRelease;
        }
        
    }
    else
    {
        //
        // The object was not found
        //

        palError = ERROR_INVALID_NAME;
    }

LocateObjectExitSHMRelease:

    SHMRelease();

LocateObjectExit:

    InternalLeaveCriticalSection(pthr, &m_csListLock);

    LOGEXIT("CSharedMemoryObjectManager::LocateObject returns %d\n", palError);

    return palError;
}

/*++
Function:
  CSharedMemoryObjectManager::ObtainHandleForObject

  Allocated a new handle for an object

Parameters:
  pthr -- thread data for calling thread
  pobj -- the object to allocate a handle for
  dwRightsRequired -- the access rights to grant the handle; currently ignored
  fInheritHandle -- true if the handle is inheritable; ignored for all but file
    objects that represent pipes
  pProcessForHandle -- the process the handle is to be used from; currently
    must be NULL
  pNewHandle -- on success, receives the newly allocated handle
--*/

PAL_ERROR   
CSharedMemoryObjectManager::ObtainHandleForObject(
    CPalThread *pthr,
    IPalObject *pobj,
    DWORD dwRightsRequested,
    bool fInheritHandle,
    IPalProcess *pProcessForHandle,     // IN, OPTIONAL
    HANDLE *pNewHandle                  // OUT
    )
{
    PAL_ERROR palError = NO_ERROR;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != pobj);
    _ASSERTE(NULL != pNewHandle);

    ENTRY("CSharedMemoryObjectManager::ObtainHandleForObject "
        "(this=%p, pthr=%p, pobj=%p, dwRightsRequested=%d, "
        "fInheritHandle=%p, pProcessForHandle=%p, pNewHandle=%p)\n",
        this,
        pthr,
        pobj,
        dwRightsRequested,
        fInheritHandle,
        pProcessForHandle,
        pNewHandle
        );

    if (NULL != pProcessForHandle)
    {
        //
        // Not yet supported
        //

        ASSERT("Caller to ObtainHandleForObject provided a process\n");
        return ERROR_CALL_NOT_IMPLEMENTED;
    }

    palError = m_HandleManager.AllocateHandle(
        pthr,
        pobj,
        dwRightsRequested,
        fInheritHandle,
        pNewHandle
        );

    LOGEXIT("CSharedMemoryObjectManager::ObtainHandleForObject return %d\n", palError);

    return palError;    
}

/*++
Function:
  CSharedMemoryObjectManager::RevokeHandle

  Removes a handle from the process's handle table, which in turn releases
  the handle's reference on the object instance it refers to

Parameters:
  pthr -- thread data for calling thread
  hHandleToRevoke -- the handle to revoke
--*/

PAL_ERROR
CSharedMemoryObjectManager::RevokeHandle(
    CPalThread *pthr,
    HANDLE hHandleToRevoke
    )
{
    PAL_ERROR palError = NO_ERROR;

    _ASSERTE(NULL != pthr);

    ENTRY("CSharedMemoryObjectManager::RevokeHandle "
        "(this=%p, pthr=%p, hHandleToRevoke=%p)\n",
        this,
        pthr,
        hHandleToRevoke
        );
    
    palError = m_HandleManager.FreeHandle(pthr, hHandleToRevoke);

    LOGEXIT("CSharedMemoryObjectManager::RevokeHandle returns %d\n", palError);

    return palError;
}

/*++
Function:
  CSharedMemoryObjectManager::ReferenceObjectByHandle

  Returns a referenced object instance that a handle refers to

Parameters:
  pthr -- thread data for calling thread
  hHandleToReference -- the handle to reference
  paot -- acceptable types for the underlying object
  dwRightsRequired -- the access rights that the handle must have been
    granted; currently ignored
  ppobj -- on success, receives a reference to the object instance
--*/

PAL_ERROR
CSharedMemoryObjectManager::ReferenceObjectByHandle(
    CPalThread *pthr,
    HANDLE hHandleToReference,
    CAllowedObjectTypes *paot,
    DWORD dwRightsRequired,
    IPalObject **ppobj               // OUT
    )
{
    PAL_ERROR palError;
    DWORD dwRightsGranted;
    IPalObject *pobj;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != paot);
    _ASSERTE(NULL != ppobj);

    ENTRY("CSharedMemoryObjectManager::ReferenceObjectByHandle "
        "(this=%p, pthr=%p, hHandleToReference=%p, paot=%p, "
        "dwRightsRequired=%d, ppobj=%p)\n",
        this,
        pthr,
        hHandleToReference,
        paot,
        dwRightsRequired,
        ppobj
        );

    palError = m_HandleManager.GetObjectFromHandle(
        pthr,
        hHandleToReference,
        &dwRightsGranted,
        &pobj
        );

    if (NO_ERROR == palError)
    {
        palError = CheckObjectTypeAndRights(
            pobj,
            paot,
            dwRightsGranted,
            dwRightsRequired
            );

        if (NO_ERROR == palError)
        {
            //
            // Transfer object reference to out parameter
            //

            *ppobj = pobj;
        }
        else
        {
            pobj->ReleaseReference(pthr);
        }
    }

    LOGEXIT("CSharedMemoryObjectManager::ReferenceObjectByHandle returns %d\n",
        palError
        );

    return palError;    
}

/*++
Function:
  CSharedMemoryObjectManager::ReferenceObjectByHandleArray

  Returns the referenced object instances that an array of handles
  refer to.

Parameters:
  pthr -- thread data for calling thread
  rgHandlesToReference -- the array of handles to reference
  dwHandleCount -- the number of handles in the arrayu
  paot -- acceptable types for the underlying objects
  dwRightsRequired -- the access rights that the handles must have been
    granted; currently ignored
  rgpobjs -- on success, receives references to the object instances; will
    be empty on failures
--*/

PAL_ERROR
CSharedMemoryObjectManager::ReferenceMultipleObjectsByHandleArray(
    CPalThread *pthr,
    HANDLE rghHandlesToReference[],
    DWORD dwHandleCount,
    CAllowedObjectTypes *paot,
    DWORD dwRightsRequired,
    IPalObject *rgpobjs[]            // OUT (caller allocated)
    )
{
    PAL_ERROR palError = NO_ERROR;
    IPalObject *pobj = NULL;
    DWORD dwRightsGranted;
    DWORD dw;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != rghHandlesToReference);
    _ASSERTE(0 < dwHandleCount);
    _ASSERTE(NULL != paot);
    _ASSERTE(NULL != rgpobjs);

    ENTRY("CSharedMemoryObjectManager::ReferenceMultipleObjectsByHandleArray "
        "(this=%p, pthr=%p, rghHandlesToReference=%p, dwHandleCount=%d, "
        "pAllowedTyped=%d, dwRightsRequired=%d, rgpobjs=%p)\n",
        this,
        pthr,
        rghHandlesToReference,
        dwHandleCount,
        paot,
        dwRightsRequired,
        rgpobjs
        );

    m_HandleManager.Lock(pthr);

    for (dw = 0; dw < dwHandleCount; dw += 1)
    {        
        palError = m_HandleManager.GetObjectFromHandle(
            pthr,
            rghHandlesToReference[dw],
            &dwRightsGranted,
            &pobj
            );

        if (NO_ERROR == palError)
        {
            palError = CheckObjectTypeAndRights(
                pobj,
                paot,
                dwRightsGranted,
                dwRightsRequired
                );

            if (NO_ERROR == palError)
            {
                //
                // Transfer reference to out array
                //

                rgpobjs[dw] = pobj;
                pobj = NULL;
            }
        }

        if (NO_ERROR != palError)
        {
            break;
        }
    }

    //
    // The handle manager lock must be released before releasing
    // any object references, as ReleaseReference will acquire
    // the object manager list lock (which needs to be acquired before
    // the handle manager lock)
    //

    m_HandleManager.Unlock(pthr);

    if (NO_ERROR != palError)
    {
        //
        // dw's current value is the failing index, so we want
        // to free from dw - 1.
        //
        
        while (dw > 0)
        {
            rgpobjs[--dw]->ReleaseReference(pthr);
        }

        if (NULL != pobj)
        {
            pobj->ReleaseReference(pthr);
        }
    }

    LOGEXIT("CSharedMemoryObjectManager::ReferenceMultipleObjectsByHandleArray"
        " returns %d\n",
        palError
        );

    return palError;
}

/*++
Function:
  CSharedMemoryObjectManager::ReferenceObjectByForeignHandle

  Returns a referenced object instance that a handle belongin to
  another process refers to; currently unimplemented

Parameters:
  pthr -- thread data for calling thread
  hForeignHandle -- the handle to reference
  pForeignProcess -- the process that hForeignHandle belongs to
  paot -- acceptable types for the underlying object
  dwRightsRequired -- the access rights that the handle must have been
    granted; currently ignored
  ppobj -- on success, receives a reference to the object instance
--*/

PAL_ERROR
CSharedMemoryObjectManager::ReferenceObjectByForeignHandle(
    CPalThread *pthr,
    HANDLE hForeignHandle,
    IPalProcess *pForeignProcess,
    CAllowedObjectTypes *paot,
    DWORD dwRightsRequired,
    IPalObject **ppobj               // OUT
    )
{
    //
    // Not implemented for basic shared memory object manager --
    // requires an IPC channel. (For the shared memory object manager
    // PAL_LocalHandleToRemote and PAL_RemoteHandleToLocal must still
    // be used...)
    //

    ASSERT("ReferenceObjectByForeignHandle not yet supported\n");
    return ERROR_CALL_NOT_IMPLEMENTED;
}

/*++
Function:
  CSharedMemoryObjectManager::ImportSharedObjectIntoProcess

  Takes an object's shared memory data and from it creates the
  necessary in-process structures for the object

Parameters:
  pthr -- thread data for calling thread
  pot -- the object's type
  poa -- attributes for the object
  shmSharedObjectData -- the shared memory pointer for the object's shared
    data
  psmod -- the shared memory data for the object, mapped into this process's
    address space
  fAddRefSharedData -- if TRUE, we need to add to the shared data reference
    count
  ppshmobj -- on success, receives a pointer to the newly created local
    object instance
--*/

PAL_ERROR
CSharedMemoryObjectManager::ImportSharedObjectIntoProcess(
    CPalThread *pthr,
    CObjectType *pot,
    CObjectAttributes *poa,
    SHMPTR shmSharedObjectData,
    SHMObjData *psmod,
    bool fAddRefSharedData,
    CSharedMemoryObject **ppshmobj
    )
{
    PAL_ERROR palError = NO_ERROR;
    CSharedMemoryObject *pshmobj;
    PLIST_ENTRY pleObjectList;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != pot);
    _ASSERTE(NULL != poa);
    _ASSERTE(SHMNULL != shmSharedObjectData);
    _ASSERTE(NULL != psmod);
    _ASSERTE(NULL != ppshmobj);

    ENTRY("CSharedMemoryObjectManager::ImportSharedObjectIntoProcess(pthr=%p, "
        "pot=%p, poa=%p, shmSharedObjectData=%p, psmod=%p, fAddRefSharedData=%d, "
        "ppshmobj=%p)\n",
        pthr,
        pot,
        poa,
        shmSharedObjectData,
        psmod,
        fAddRefSharedData,
        ppshmobj
        );
    
    if (CObjectType::WaitableObject == pot->GetSynchronizationSupport())
    {
        pshmobj = InternalNew<CSharedMemoryWaitableObject>(pot,
                                                           &m_csListLock,
                                                           shmSharedObjectData,
                                                           psmod,
                                                           fAddRefSharedData);
    }
    else
    {
        pshmobj = InternalNew<CSharedMemoryObject>(pot,
                                                   &m_csListLock,
                                                   shmSharedObjectData,
                                                   psmod,
                                                   fAddRefSharedData);
    }

    if (NULL != pshmobj)
    {
        palError = pshmobj->InitializeFromExistingSharedData(pthr, poa);
        if (NO_ERROR == palError)
        {
            if (0 != psmod->dwNameLength)
            {
                pleObjectList = &m_leNamedObjects;
            }
            else
            {
                pleObjectList = &m_leAnonymousObjects;
            }
            
            InsertTailList(pleObjectList, pshmobj->GetObjectListLink());
        }
        else
        {
            goto ImportSharedObjectIntoProcessExit;
        }
    }
    else
    {
        ERROR("Unable to alllocate new object\n");
        palError = ERROR_OUTOFMEMORY;
        goto ImportSharedObjectIntoProcessExit;
    }

    *ppshmobj = pshmobj;

ImportSharedObjectIntoProcessExit:

    LOGEXIT("CSharedMemoryObjectManager::ImportSharedObjectIntoProcess returns %d\n", palError);

    return palError;
}

static PalObjectTypeId RemotableObjectTypes[] =
    {otiManualResetEvent, otiAutoResetEvent, otiMutex, otiProcess};
    
static CAllowedObjectTypes aotRemotable PAL_GLOBAL (
    RemotableObjectTypes,
    sizeof(RemotableObjectTypes) / sizeof(RemotableObjectTypes[0])
    );

/*++
Function:
  PAL_LocalHandleToRemote

  Returns a "remote handle" that may be passed to another process.

Parameters:
  hLocal -- the handle to generate a "remote handle" for
--*/

PALIMPORT
RHANDLE
PALAPI
PAL_LocalHandleToRemote(IN HANDLE hLocal)
{
    PAL_ERROR palError = NO_ERROR;
    CPalThread *pthr;
    IPalObject *pobj = NULL;
    CSharedMemoryObject *pshmobj;
    SHMObjData *psmod = NULL;
    RHANDLE hRemote = reinterpret_cast<RHANDLE>(INVALID_HANDLE_VALUE);

    PERF_ENTRY(PAL_LocalHandleToRemote);
    ENTRY("PAL_LocalHandleToRemote( hLocal=0x%lx )\n", hLocal);

    pthr = InternalGetCurrentThread();

    if (!HandleIsSpecial(hLocal))
    {
        palError = g_pObjectManager->ReferenceObjectByHandle(
            pthr,
            hLocal,
            &aotRemotable,
            0,
            &pobj
            );

        if (NO_ERROR != palError)
        {
            goto PAL_LocalHandleToRemoteExitNoLockRelease;
        }
    }
    else if (hPseudoCurrentProcess == hLocal)
    {
        pobj = g_pobjProcess;
        pobj->AddReference();
    }
    else
    {
        ASSERT("Invalid special handle type passed to PAL_LocalHandleToRemote\n");
        palError = ERROR_INVALID_HANDLE;
        goto PAL_LocalHandleToRemoteExitNoLockRelease;
    }

    pshmobj = static_cast<CSharedMemoryObject*>(pobj);

    //
    // Make sure that the object is shared
    //

    palError = pshmobj->EnsureObjectIsShared(pthr);
    if (NO_ERROR != palError)
    {
        ERROR("Failure %d promoting object\n", palError);
        goto PAL_LocalHandleToRemoteExitNoLockRelease;
    }

    SHMLock();
    
    psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
    if (NULL != psmod)
    {
        //
        // Bump up the process ref count by 1. The receiving process will not
        // increase the ref count when it converts the remote handle to
        // local.
        //
        
        psmod->lProcessRefCount += 1;

        //
        // The remote handle is simply the SHMPTR for the SHMObjData
        //

        hRemote = reinterpret_cast<RHANDLE>(pshmobj->GetShmObjData());
    }
    else
    {
        ASSERT("Unable to map shared object data\n");
        palError = ERROR_INTERNAL_ERROR;
        goto PAL_LocalHandleToRemoteExit;
    }

PAL_LocalHandleToRemoteExit:

    SHMRelease();

PAL_LocalHandleToRemoteExitNoLockRelease:

    if (NULL != pobj)
    {
        pobj->ReleaseReference(pthr);
    }

    if (NO_ERROR != palError)
    {
        pthr->SetLastError(palError);
    }
    
    LOGEXIT("PAL_LocalHandleToRemote returns RHANDLE 0x%lx\n", hRemote);
    PERF_EXIT(PAL_LocalHandleToRemote);
    return hRemote;
}

/*++
Function:
  CSharedMemoryObjectManager::ConvertRemoteHandleToLocal

  Given a "remote handle" creates a local handle that refers
  to the desired object. (Unlike PAL_RemoteHandleToLocal this method
  needs to access internal object manager state, so it's a member function.)

Parameters:
  pthr -- thread data for calling thread
  rhRemote -- the remote handle
  phLocal -- on success, receives the local handle
--*/

PAL_ERROR
CSharedMemoryObjectManager::ConvertRemoteHandleToLocal(
    CPalThread *pthr,
    RHANDLE rhRemote,
    HANDLE *phLocal
    )
{
    PAL_ERROR palError = NO_ERROR;
    SHMObjData *psmod;
    CSharedMemoryObject *pshmobj = NULL;
    PLIST_ENTRY pleObjectList;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != phLocal);

    ENTRY("CSharedMemoryObjectManager::ConvertRemoteHandleToLocal "
        "(this=%p, pthr=%p, rhRemote=%p, phLocal=%p)\n",
        this,
        pthr,
        rhRemote,
        phLocal
        );

    if (rhRemote == NULL || rhRemote == INVALID_HANDLE_VALUE)
    {
        palError = ERROR_INVALID_HANDLE;
        goto ConvertRemoteHandleToLocalExitNoLockRelease;
    }

    InternalEnterCriticalSection(pthr, &m_csListLock);
    SHMLock();

    //
    // The remote handle is really a shared memory pointer to the
    // SHMObjData for the object.
    //

    psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, reinterpret_cast<SHMPTR>(rhRemote));
    if (NULL == psmod)
    {
        ERROR("Invalid remote handle\n");
        palError = ERROR_INVALID_HANDLE;
        goto ConvertRemoteHandleToLocalExit;
    }

    //
    // Check to see if a local reference for this object already
    // exists
    //

    if (0 != psmod->dwNameLength)
    {
        pleObjectList = &m_leNamedObjects;
    }
    else
    {
        pleObjectList = &m_leAnonymousObjects;
    }

    for (PLIST_ENTRY ple = pleObjectList->Flink;
         ple != pleObjectList;
         ple = ple->Flink)
    {
        pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);

        if (SharedObject == pshmobj->GetObjectDomain()
            && reinterpret_cast<SHMPTR>(rhRemote) == pshmobj->GetShmObjData())
        {
            TRACE("Object for remote handle already present in this process\n");
            
            //
            // PAL_LocalHandleToRemote bumped up the process refcount on the
            // object. Since this process already had a reference to the object
            // we need to decrement that reference now...
            //

            psmod->lProcessRefCount -= 1;
            _ASSERTE(0 < psmod->lProcessRefCount);

            //
            // We also need to add a reference to the object (since ReleaseReference
            // gets called below)
            //

            pshmobj->AddReference();

            break;
        }

        pshmobj = NULL;
    }

    if (NULL == pshmobj)
    {
        CObjectType *pot;
        CObjectAttributes oa;
        
        //
        // Get the local instance of the CObjectType
        //
        
        pot = CObjectType::GetObjectTypeById(psmod->eTypeId);
        if (NULL == pot)
        {
            ASSERT("Invalid object type ID in shared memory info\n");
            goto ConvertRemoteHandleToLocalExit;
        }
        
        //
        // Create the local state for the shared object
        //

        palError = ImportSharedObjectIntoProcess(
            pthr,
            pot,
            &oa,
            reinterpret_cast<SHMPTR>(rhRemote),
            psmod,
            FALSE,
            &pshmobj
            );

        if (NO_ERROR != palError)
        {
            goto ConvertRemoteHandleToLocalExit;
        }        
    }

    //
    // Finally, allocate a local handle for the object
    //

    palError = ObtainHandleForObject(
        pthr,
        pshmobj,
        0,
        FALSE,
        NULL,
        phLocal
        );
        
ConvertRemoteHandleToLocalExit:

    SHMRelease();
    InternalLeaveCriticalSection(pthr, &m_csListLock);

ConvertRemoteHandleToLocalExitNoLockRelease:

    if (NULL != pshmobj)
    {
        pshmobj->ReleaseReference(pthr);
    }

    LOGEXIT("CSharedMemoryObjectManager::ConvertRemoteHandleToLocal returns %d\n", palError);

    return palError;
}

/*++
Function:
  PAL_RemoteHandleToLocal

  Given a "remote handle", return a local handle that refers to the
  specified process. Calls
  SharedMemoryObjectManager::ConvertRemoteHandleToLocal to do the actual
  work

Parameters:
  rhRemote -- the "remote handle" to convert to a local handle
--*/

PALIMPORT
HANDLE
PALAPI
PAL_RemoteHandleToLocal(IN RHANDLE rhRemote)
{
    PAL_ERROR palError = NO_ERROR;
    CPalThread *pthr;
    HANDLE hLocal = INVALID_HANDLE_VALUE;

    PERF_ENTRY(PAL_RemoteHandleToLocal);
    ENTRY("PAL_RemoteHandleToLocal( hRemote=0x%lx )\n", rhRemote);

    pthr = InternalGetCurrentThread();

    palError = static_cast<CSharedMemoryObjectManager*>(g_pObjectManager)->ConvertRemoteHandleToLocal(
        pthr,
        rhRemote,
        &hLocal
        );

    if (NO_ERROR != palError)
    {
        pthr->SetLastError(palError);
    }
    
    LOGEXIT("PAL_RemoteHandleToLocal returns HANDLE 0x%lx\n", hLocal);
    PERF_EXIT(PAL_RemoteHandleToLocal);
    return hLocal;
}

/*++
Function:
  CheckObjectTypeAndRights

  Helper routine that determines if:
  1) An object instance is of a specified type
  2) A set of granted access rights satisfies the required access rights
     (currently ignored)

Parameters:
  pobj -- the object instance whose type is to be checked
  paot -- the acceptable type for the object instance
  dwRightsGranted -- the granted access rights (ignored)
  dwRightsRequired -- the required access rights (ignored)
--*/

static
PAL_ERROR
CheckObjectTypeAndRights(
    IPalObject *pobj,
    CAllowedObjectTypes *paot,
    DWORD dwRightsGranted,
    DWORD dwRightsRequired
    )
{
    PAL_ERROR palError = NO_ERROR;

    _ASSERTE(NULL != pobj);
    _ASSERTE(NULL != paot);

    ENTRY("CheckObjectTypeAndRights (pobj=%p, paot=%p, "
        "dwRightsGranted=%d, dwRightsRequired=%d)\n",
        pobj,
        paot,
        dwRightsGranted,
        dwRightsRequired
        );

    if (paot->IsTypeAllowed(pobj->GetObjectType()->GetId()))
    {
#ifdef ENFORCE_OBJECT_ACCESS_RIGHTS

        //
        // This is where the access right check would occur if Win32 object
        // security were supported.
        //
        
        if ((dwRightsRequired & dwRightsGranted) != dwRightsRequired)
        {
            palError = ERROR_ACCESS_DENIED;
        }
#endif
    }
    else
    {
        palError = ERROR_INVALID_HANDLE;
    }

    LOGEXIT("CheckObjectTypeAndRights returns %d\n", palError);

    return palError;
}
    

