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

/*++



Module Name:

    shmfilelockmgr.cpp

Abstract:
    Shared memory based file lock manager



--*/

#include "pal/thread.hpp"
#include "pal/malloc.hpp"
#include "pal/dbgmsg.h"
#include "shmfilelockmgr.hpp"

using namespace CorUnix;

SET_DEFAULT_DEBUG_CHANNEL(FILE);

PAL_ERROR
FILEAddNewLockedRgn(
    SHMFILELOCKS* fileLocks,
    PVOID pvControllerInstance, 
    SHMFILELOCKRGNS *insertAfter,
    UINT64 lockRgnStart, 
    UINT64 nbBytesToLock, 
    LOCK_TYPE lockType
    );

PAL_ERROR
FILELockFileRegion(
    SHMPTR shmFileLocks,
    PVOID pvControllerInstance,
    UINT64 lockRgnStart, 
    UINT64 nbBytesToLock,
    LOCK_TYPE lockAction
    );

PAL_ERROR
FILEUnlockFileRegion(
    SHMPTR shmFileLocks,
    PVOID pvControllerInstance, 
    UINT64 unlockRgnStart, 
    UINT64 nbBytesToUnlock,
    LOCK_TYPE unlockType
    );

void
FILECleanUpLockedRgn(
    SHMPTR shmFileLocks,
    DWORD dwAccessRights,
    PVOID pvControllerInstance
    );

PAL_ERROR
FILEGetSHMFileLocks(
    LPCSTR filename,
    SHMPTR *pshmFileLocks,
    BOOL noCreate
    );

/* return TRUE if LockToTest region is behind lockRgn, FALSE otherwise */
#define IS_LOCK_BEFORE(LockToTest, lockRgn) \
    (((LockToTest)->lockRgnStart + (LockToTest)->nbBytesLocked) <= \
                                                        (lockRgn)->lockRgnStart)

/* return TRUE if LockToTest region intersect with lockRgn, FALSE otherwise */
#define IS_LOCK_INTERSECT(LockToTest, lockRgn) \
    (!IS_LOCK_BEFORE(LockToTest, lockRgn) && !IS_LOCK_BEFORE(lockRgn, LockToTest))

/* return TRUE if LockToTest region and lockRgn have the same file pointer and 
   the same process Id, FALSE otherwise */
#define IS_LOCK_HAVE_SAME_OWNER(LockToTest, lockRgn) \
    (((LockToTest)->pvControllerInstance == (lockRgn)->pvControllerInstance) && \
     ((LockToTest)->processId == (lockRgn)->processId))

/* return TRUE if LockToTest region and lockRgn represent the same lock, 
   FALSE otherwise*/
#define IS_LOCK_EQUAL(LockToTest, lockRgn) \
        (((LockToTest)->processId == (lockRgn)->processId)           && \
         ((LockToTest)->pvControllerInstance == (lockRgn)->pvControllerInstance)   && \
         ((LockToTest)->lockRgnStart == (lockRgn)->lockRgnStart)     && \
         ((LockToTest)->nbBytesLocked == (lockRgn)->nbBytesLocked)   && \
         ((LockToTest)->lockType == (lockRgn)->lockType))

PAL_ERROR
CSharedMemoryFileLockMgr::GetLockControllerForFile(
    CPalThread *pThread,                // IN, OPTIONAL
    LPCSTR szFileName,
    DWORD dwAccessRights,
    DWORD dwShareMode,
    IFileLockController **ppLockController  // OUT
    )
{
    PAL_ERROR palError = NO_ERROR;
    SHMPTR shmFileLocks = SHMNULL;
    SHMFILELOCKS* fileLocks = NULL;
    CSharedMemoryFileLockController *pController = NULL;

    SHMLock();

    palError = FILEGetSHMFileLocks(szFileName, &shmFileLocks, FALSE);
    if (NO_ERROR != palError)
    {
        goto GetLockControllerForFileExit;
    }

    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE || fileLocks == NULL)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto GetLockControllerForFileExit;
    }
        
    if(SHARE_MODE_NOT_INITALIZED == fileLocks->share_mode)
    {
        /* this is the first time this file is open */
        fileLocks->share_mode = (int) dwShareMode;
    }
    /* start checking for dwDesired access and dwShareMode conditions */
    else if(0 == fileLocks->share_mode)
    {
        /* file is exclusively locked */
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;
    }
    /* check for if the desired access is allowed by the share mode */
    else if( (dwAccessRights & GENERIC_READ) && 
             !(fileLocks->share_mode & FILE_SHARE_READ) )
    {
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;     
    }
    else if( (dwAccessRights & GENERIC_WRITE) && 
             !(fileLocks->share_mode & FILE_SHARE_WRITE) )
    {
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;     
    }
    /* The case when changing to a conflicting share mode is particular.
       The general rule is: changing from conflicting share mode is invalid
       (i.e changing from FILE_SHARE_WRITE to FILE_SHARE_READ is invalid).
       However, if one of the share flags is the same
       (i.e changing from FILE_SHARE_WRITE to FILE_SHARE_READ | FILE_SHARE_WRITE)
       the result is valid. (Please note that FILE_SHARE_READ is ignored
       in this case).
    */
    else if( (dwShareMode & FILE_SHARE_READ) && 
             !(dwShareMode & FILE_SHARE_WRITE) &&
             !(fileLocks->share_mode & FILE_SHARE_READ))
                         
    {
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;     
    }
    else if( (dwShareMode & FILE_SHARE_WRITE) && 
             !(dwShareMode & FILE_SHARE_READ) &&
             !(fileLocks->share_mode & FILE_SHARE_WRITE))
    {
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;     
    }
    /* Changing to a less permissive sharing permissions is valid
       if the file handle doesn't have an access right that conflicts with
       the sharing permissions we are trying to set
       (ex: changing from FILE_SHARE_READ|FILE_SHARE_WRITE to FILE_SHARE_WRITE
       isn't valid if the file descriptor still has a GENERIC_READ permission).
    */     
    else if( (fileLocks->nbReadAccess) && 
             !(dwShareMode & FILE_SHARE_READ) )
    {
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;     
    }
    else if( (fileLocks->nbWriteAccess) && 
             !(dwShareMode & FILE_SHARE_WRITE) )
    {
        palError = ERROR_SHARING_VIOLATION;
        goto GetLockControllerForFileExit;     
    }

    /* we are trying to change to a less restrictive sharing permission set 
       keep the current permissions */  
    if( (dwShareMode & FILE_SHARE_READ) && 
              !(fileLocks->share_mode & FILE_SHARE_READ) )
    {
        dwShareMode = fileLocks->share_mode;
    }

    if( (dwShareMode & FILE_SHARE_WRITE) && 
              !(fileLocks->share_mode & FILE_SHARE_WRITE) )
    {
        dwShareMode = fileLocks->share_mode;
    }

    pController = InternalNew<CSharedMemoryFileLockController>(dwAccessRights, shmFileLocks);
    if (NULL == pController)
    {
        palError = ERROR_OUTOFMEMORY;
        goto GetLockControllerForFileExit;
    }

    //
    // pController now owns the shared memory pointer, so make sure we
    // don't attempt to free it below.
    //

    shmFileLocks = SHMNULL;

    /* set the share mode again, it's possible that the share mode is now more
    restrictive than the previous mode set. */
    fileLocks->share_mode = dwShareMode;
    if( dwAccessRights & GENERIC_READ )
    {
        fileLocks->nbReadAccess++;  
    }
    if( dwAccessRights & GENERIC_WRITE )
    {
        fileLocks->nbWriteAccess++;
    }

    // ************** NOTE **************
    // If you add any error paths after this point you must communicate the value of dwAccessRights to
    // FILECleanUpLockedRgn() in the cleanup code below so that it can correctly undo the changes to
    // fileLocks->nbReadAccess and nbWriteAccess made above.
    // ************** NOTE **************

GetLockControllerForFileExit:

    if (NO_ERROR == palError)
    {
        *ppLockController = pController;
    }
    else
    {
        if (NULL != pController)
        {
            pController->ReleaseController();
        }

        if (SHMNULL != shmFileLocks)
        {
            FILECleanUpLockedRgn(
                shmFileLocks,
                0,
                NULL
                );
        }
    }

    SHMRelease();

    return palError;
}

PAL_ERROR
CSharedMemoryFileLockMgr::GetFileShareModeForFile(
   LPCSTR szFileName,
   DWORD* pdwShareMode)
{
    PAL_ERROR palError = NO_ERROR;
    *pdwShareMode = SHARE_MODE_NOT_INITALIZED;
    SHMPTR shmFileLocks = SHMNULL;
    SHMFILELOCKS* fileLocks = NULL;

    SHMLock();

    palError = FILEGetSHMFileLocks(szFileName, &shmFileLocks, TRUE);
    if (NO_ERROR != palError || shmFileLocks == SHMNULL)
    {
        goto GetLockControllerForFileExit;
    }

    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto GetLockControllerForFileExit;
    }

    *pdwShareMode = fileLocks->share_mode;

GetLockControllerForFileExit:

    if (SHMNULL != shmFileLocks)
    {
      FILECleanUpLockedRgn(
                shmFileLocks,
                0,
                NULL
                );
    }

    SHMRelease();

    return palError;
}

PAL_ERROR
CSharedMemoryFileLockController::GetTransactionLock(
    CPalThread *pThread,                // IN, OPTIONAL
    FileTransactionLockType eLockType,
    DWORD dwOffsetLow,
    DWORD dwOffsetHigh,
    DWORD nNumberOfBytesToLockLow,
    DWORD nNumberOfBytesToLockHigh,
    IFileTransactionLock **ppTransactionLock    // OUT
    )
{
    PAL_ERROR palError = NO_ERROR;
    UINT64 lockRgnStart;
    UINT64 nbBytesToLock;

    lockRgnStart  = ((UINT64)dwOffsetHigh) << 32  | dwOffsetLow;
    nbBytesToLock = ((UINT64)nNumberOfBytesToLockHigh) << 32  | 
                             nNumberOfBytesToLockLow;

    palError = FILELockFileRegion(
        m_shmFileLocks,
        reinterpret_cast<PVOID>(this),
        lockRgnStart, 
        nbBytesToLock,
        RDWR_LOCK_RGN
        );

    if (NO_ERROR == palError)
    {
        *ppTransactionLock = InternalNew<CSharedMemoryFileTransactionLock>(m_shmFileLocks,
                                                                           reinterpret_cast<PVOID>(this),
                                                                           lockRgnStart, 
                                                                           nbBytesToLock);
        if (NULL == *ppTransactionLock)
        {
            palError = ERROR_OUTOFMEMORY;
            FILEUnlockFileRegion(
                m_shmFileLocks,
                reinterpret_cast<PVOID>(this),
                lockRgnStart, 
                nbBytesToLock,
                RDWR_LOCK_RGN
                );
        }
    }

    return palError;
}

PAL_ERROR
CSharedMemoryFileLockController::CreateFileLock(
    CPalThread *pThread,                // IN, OPTIONAL
    DWORD dwOffsetLow,
    DWORD dwOffsetHigh,
    DWORD nNumberOfBytesToLockLow,
    DWORD nNumberOfBytesToLockHigh,
    FileLockExclusivity eFileLockExclusivity,
    FileLockWaitMode eFileLockWaitMode
    )
{
    PAL_ERROR palError = NO_ERROR;
    UINT64 lockRgnStart;
    UINT64 nbBytesToLock;

    if (ExclusiveFileLock != eFileLockExclusivity
        || FailImmediately != eFileLockWaitMode)
    {
        ASSERT("LockFileEx functionality not yet supported");
        palError = ERROR_NOT_SUPPORTED;
        goto CreateFileLockExit;
    }

    lockRgnStart  = ((UINT64)dwOffsetHigh) << 32  | dwOffsetLow;
    nbBytesToLock = ((UINT64)nNumberOfBytesToLockHigh) << 32  | 
                             nNumberOfBytesToLockLow;

    palError = FILELockFileRegion(
        m_shmFileLocks,
        reinterpret_cast<PVOID>(this),
        lockRgnStart, 
        nbBytesToLock,
        USER_LOCK_RGN
        );

CreateFileLockExit:

    return palError;
}

PAL_ERROR
CSharedMemoryFileLockController::ReleaseFileLock(
    CPalThread *pThread,                // IN, OPTIONAL
    DWORD dwOffsetLow,
    DWORD dwOffsetHigh,
    DWORD nNumberOfBytesToUnlockLow,
    DWORD nNumberOfBytesToUnlockHigh
    )
{
    PAL_ERROR palError = NO_ERROR;
    UINT64 unlockRgnStart;
    UINT64 nbBytesToUnlock;

    unlockRgnStart  = ((UINT64)dwOffsetHigh) << 32  | dwOffsetLow;
    nbBytesToUnlock = ((UINT64)nNumberOfBytesToUnlockHigh) << 32  | 
                             nNumberOfBytesToUnlockLow;

    palError = FILEUnlockFileRegion(
        m_shmFileLocks,
        reinterpret_cast<PVOID>(this),
        unlockRgnStart, 
        nbBytesToUnlock,
        USER_LOCK_RGN
        );

    return palError;
}

void
CSharedMemoryFileLockController::ReleaseController()
{
    if (SHMNULL != m_shmFileLocks)
    {
        FILECleanUpLockedRgn(
            m_shmFileLocks,
            m_dwAccessRights,
            reinterpret_cast<PVOID>(this)
            );
    }

    InternalDelete(this);
}

void
CSharedMemoryFileTransactionLock::ReleaseLock()
{
    FILEUnlockFileRegion(
        m_shmFileLocks,
        m_pvControllerInstance,
        m_lockRgnStart, 
        m_nbBytesToLock,
        RDWR_LOCK_RGN
        );

    InternalDelete(this);
}

PAL_ERROR
FILELockFileRegion(
    SHMPTR shmFileLocks,
    PVOID pvControllerInstance,
    UINT64 lockRgnStart, 
    UINT64 nbBytesToLock,
    LOCK_TYPE lockAction
    )
{
    PAL_ERROR palError = NO_ERROR;
    SHMFILELOCKRGNS *curLock, *prevLock, *insertAfter, 
                     lockRgn, fakeLock = {0,0,0,0};
    SHMFILELOCKS *fileLocks;

    SHMLock();
    
    /* nothing to do if the region to lock is empty */
    if (nbBytesToLock == 0) 
    {
        TRACE("Locking an empty region (%I64d, %I64d)\n", lockRgnStart, nbBytesToLock);
        goto EXIT;
    }
    
    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE || fileLocks == NULL)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto EXIT;
    }

    if (fileLocks->fileLockedRgns != 0)
    {        
        prevLock = &fakeLock;
        
        if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, fileLocks->fileLockedRgns) 
            == FALSE)
        {
            ASSERT("Unable to get pointer from shm pointer.\n");
            palError = ERROR_INTERNAL_ERROR;
            goto EXIT;
        }
        
        lockRgn.lockRgnStart = lockRgnStart;
        lockRgn.nbBytesLocked = nbBytesToLock;
        lockRgn.pvControllerInstance = pvControllerInstance;
        lockRgn.processId = GetCurrentProcessId();
        lockRgn.lockType = lockAction;

        while((curLock != NULL) && IS_LOCK_BEFORE(curLock, &lockRgn))
        {            
            prevLock = curLock; 
            if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, curLock->next) == FALSE)
            {
                ASSERT("Unable to get pointer from shm pointer.\n");
                palError = ERROR_INTERNAL_ERROR;
                goto EXIT;
            }
        }
                
        while((curLock != NULL) && IS_LOCK_INTERSECT(curLock, &lockRgn))
        {
            /* we couldn't lock the requested region if it overlap with other 
               region locked explicitly (by LockFile call) by other file pointer */
            if ((lockAction == USER_LOCK_RGN) || 
                ((curLock->lockType  == USER_LOCK_RGN) && 
                 !IS_LOCK_HAVE_SAME_OWNER(curLock, &lockRgn)))
            {
                WARN("The requested lock region overlaps an existing locked region\n");
                palError = ERROR_LOCK_VIOLATION;
                goto EXIT;
            }
            
            prevLock = curLock; 
            if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, curLock->next) == FALSE)
            {
                ASSERT("Unable to get pointer from shm pointer.\n");
                palError = ERROR_INTERNAL_ERROR;
                goto EXIT;
            }
        }
        
        /* save the previous lock in case we need to insert the requested lock */
        insertAfter = prevLock;         
                
        while(((curLock != NULL) && IS_LOCK_INTERSECT(&lockRgn, curLock)))
        {
            /* we couldn't lock the requested region if it overlap with other region 
               locked explicitly (by LockFile call) by other file pointer */
            if ((lockAction == USER_LOCK_RGN) || 
                ((curLock->lockType  == USER_LOCK_RGN) &&  
                 !IS_LOCK_HAVE_SAME_OWNER(curLock, &lockRgn)))
            {
                WARN("The requested lock region overlaps an existing locked region\n");
                palError = ERROR_LOCK_VIOLATION;
                goto EXIT;
            }
            
            prevLock = curLock; 
            if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLock, curLock->next) == FALSE)
            {
                ASSERT("Unable to get pointer from shm pointer.\n");
                palError = ERROR_INTERNAL_ERROR;
                goto EXIT;
            }
        }
       
        if (insertAfter == &fakeLock) 
        {
            insertAfter = NULL;
        }

        palError = FILEAddNewLockedRgn(
            fileLocks,
            pvControllerInstance,
            insertAfter,
            lockRgnStart,
            nbBytesToLock,
            lockAction
            );

        if (NO_ERROR != palError)
        {
            WARN("Couldn't add the new locked region into SHM\n");
            goto EXIT;
        }
    }
    else /* lock region list is empty. */
    {
        palError = FILEAddNewLockedRgn(
            fileLocks,
            pvControllerInstance,
            NULL,
            lockRgnStart,
            nbBytesToLock,
            lockAction
            );
        
        if (NO_ERROR != palError)
        {
            ERROR("Couldn't add the first file locked region \n");
            goto EXIT;
        }
    }  

EXIT:
    SHMRelease();
    return palError;
}

PAL_ERROR
FILEUnlockFileRegion(
    SHMPTR shmFileLocks,
    PVOID pvControllerInstance,
    UINT64 unlockRgnStart, 
    UINT64 nbBytesToUnlock,
    LOCK_TYPE unlockType
    )
{
    PAL_ERROR palError = NO_ERROR;
    SHMFILELOCKRGNS *prevLock = NULL, *curLockRgn = NULL, unlockRgn;
    SHMPTR shmcurLockRgn;
    SHMFILELOCKS *fileLocks;

    SHMLock();

    
    /* check if the region to unlock is empty or not */
    if (nbBytesToUnlock == 0) 
    {
        palError = ERROR_NOT_LOCKED;
        WARN("Attempt to unlock an empty region\n");
        goto EXIT;
    }
    
    if ((SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE) || 
        (fileLocks == NULL) ||
        (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, fileLocks->fileLockedRgns) == FALSE))
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto EXIT;
    }
    
    unlockRgn.processId = GetCurrentProcessId();
    unlockRgn.pvControllerInstance = pvControllerInstance;
    unlockRgn.lockRgnStart = unlockRgnStart;
    unlockRgn.nbBytesLocked = nbBytesToUnlock;
    unlockRgn.lockType = unlockType;

    shmcurLockRgn = fileLocks->fileLockedRgns;
    
    while((curLockRgn != NULL) && !IS_LOCK_EQUAL(curLockRgn, &unlockRgn))
    {
        prevLock = curLockRgn; 
        shmcurLockRgn = curLockRgn->next;
        if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
        {
            ASSERT("Unable to get pointer from shm pointer.\n");
            goto EXIT;
        }
    }
    
    if (curLockRgn != NULL) 
    {
        TRACE("removing the lock region (%I64u, %I64u)\n", 
               curLockRgn->lockRgnStart, curLockRgn->nbBytesLocked);

        if (prevLock == NULL) 
        {
            /* removing the first lock */
            fileLocks->fileLockedRgns = curLockRgn->next;
        }
        else
        {
            prevLock->next = curLockRgn->next;
        }
        SHMfree(shmcurLockRgn);
    }
    else
    {
        /* the lock doesn't exist */
        WARN("Attempt to unlock a non locked region\n");
        palError = ERROR_NOT_LOCKED;
        goto EXIT;
    }
    
EXIT:    
    SHMRelease();
    return palError;
}


PAL_ERROR
FILEGetSHMFileLocks(
    LPCSTR filename,
    SHMPTR *pshmFileLocks,
    BOOL noCreate
    )
{
    PAL_ERROR palError = NO_ERROR;
    SHMPTR shmPtrRet = 0;
    SHMFILELOCKS *filelocksPtr, *nextFilelocksPtr;
    char *unix_filename;

    SHMLock();

    shmPtrRet = SHMGetInfo(SIID_FILE_LOCKS);

    while(shmPtrRet != 0)
    {        
        if ( (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, filelocksPtr, shmPtrRet) == FALSE) ||
             (SHMPTR_TO_TYPED_PTR_BOOL(char, unix_filename, filelocksPtr->unix_filename) == FALSE))
        {
            ASSERT("Unable to get pointer from shm pointer.\n");
            palError = ERROR_INTERNAL_ERROR;
            goto EXIT;
        }

        if (unix_filename == NULL)
        {
            ERROR("Unexpected lock file name value.\n");
            palError = ERROR_INTERNAL_ERROR;
            goto EXIT;
        }
        
        if (strcmp(unix_filename, filename) == 0)
        {
            filelocksPtr->refCount++;
            goto EXIT;
        }
        
        shmPtrRet = filelocksPtr->next;
    }

    /* the file has never been locked before.*/
    shmPtrRet = 0;
    if (noCreate)
    {
      goto EXIT;
    }

    TRACE("Create a new entry in the file lock list in SHM\n");

    /* Create a new entry in the file lock list in SHM */
    if ((shmPtrRet = SHMalloc(sizeof(SHMFILELOCKS))) == 0)
    {
        ERROR("Can't allocate SHMFILELOCKS structure\n");
        palError = ERROR_NOT_ENOUGH_MEMORY;
        goto EXIT;
    }

    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, filelocksPtr, shmPtrRet) == FALSE)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto CLEANUP1;
    }

    filelocksPtr->unix_filename = SHMStrDup(filename);
    if (filelocksPtr->unix_filename == 0)
    {
        ERROR("Can't allocate shared memory for filename\n");
        palError = ERROR_NOT_ENOUGH_MEMORY;
        goto CLEANUP1;
    }

    filelocksPtr->fileLockedRgns = 0;
    filelocksPtr->prev = 0;
    filelocksPtr->next = SHMGetInfo(SIID_FILE_LOCKS);
    filelocksPtr->refCount = 1;
    filelocksPtr->share_mode = SHARE_MODE_NOT_INITALIZED;
    filelocksPtr->nbReadAccess = 0;
    filelocksPtr->nbWriteAccess = 0;

    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, nextFilelocksPtr, filelocksPtr->next) == FALSE)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto CLEANUP2;
    }
    
    if (nextFilelocksPtr != NULL)
    {
        nextFilelocksPtr->prev = shmPtrRet;
    }

    SHMSetInfo(SIID_FILE_LOCKS, shmPtrRet);
    goto EXIT;

CLEANUP2:
    SHMfree(filelocksPtr->unix_filename);
CLEANUP1:
    SHMfree(shmPtrRet);
    shmPtrRet = 0;
EXIT:    
    SHMRelease();

    if (NO_ERROR == palError)
    {
        *pshmFileLocks = shmPtrRet;
    }
    
    return palError;
}

PAL_ERROR
FILEAddNewLockedRgn(
    SHMFILELOCKS* fileLocks,
    PVOID pvControllerInstance, 
    SHMFILELOCKRGNS *insertAfter,
    UINT64 lockRgnStart, 
    UINT64 nbBytesToLock, 
    LOCK_TYPE lockType
    )
{
    PAL_ERROR palError = NO_ERROR;
    SHMFILELOCKRGNS *newLockRgn, *lockRgnPtr;
    SHMPTR shmNewLockRgn = SHMNULL;

    if ((fileLocks == NULL) || (pvControllerInstance == NULL))
    {
        ASSERT("Invalid Null parameter.\n");
        return FALSE;
    }

    SHMLock();
    
    /* Create a new entry for the new locked region */
    TRACE("Create a new entry for the new lock region (%I64u %I64u)\n", 
          lockRgnStart, nbBytesToLock);
    
    if ((shmNewLockRgn = SHMalloc(sizeof(SHMFILELOCKRGNS))) == SHMNULL)
    {
        ERROR("Can't allocate SHMFILELOCKRGNS structure\n");
        palError = ERROR_NOT_ENOUGH_MEMORY;
        goto EXIT;
    }

    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, newLockRgn, shmNewLockRgn) == FALSE)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        palError = ERROR_INTERNAL_ERROR;
        goto EXIT;
    }
    
    newLockRgn->processId = GetCurrentProcessId();
    newLockRgn->pvControllerInstance = pvControllerInstance;
    newLockRgn->lockRgnStart = lockRgnStart;
    newLockRgn->nbBytesLocked = nbBytesToLock;
    newLockRgn->lockType = lockType;
    
    /* All locked regions with the same offset should be sorted ascending */
    /* the sort is based on the length of the locked byte range */
    if (insertAfter != NULL)
    {
        if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, lockRgnPtr, insertAfter->next) == FALSE)
        {
            ASSERT("Unable to get pointer from shm pointer.\n");
            palError = ERROR_INTERNAL_ERROR;
            goto EXIT;
        }
    }
    else
    {
        if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, lockRgnPtr, fileLocks->fileLockedRgns) == FALSE)
        {
            ASSERT("Unable to get pointer from shm pointer.\n");
            palError = ERROR_INTERNAL_ERROR;
            goto EXIT;
        }
    }
    
    while(lockRgnPtr != NULL)
    {
        if ( (lockRgnPtr->lockRgnStart == newLockRgn->lockRgnStart) &&
             (newLockRgn->nbBytesLocked > lockRgnPtr->nbBytesLocked))
        {
            insertAfter = lockRgnPtr;
            if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, lockRgnPtr, lockRgnPtr->next) == FALSE)
            {
                ASSERT("Unable to get pointer from shm pointer.\n");
                palError = ERROR_INTERNAL_ERROR;
                goto EXIT;
            }
            continue;
        }

        break;
    }
    
    if (insertAfter != NULL)
    {       
        TRACE("Adding lock after the lock rgn (%I64d %I64d)\n", 
              insertAfter->lockRgnStart,insertAfter->nbBytesLocked);
        newLockRgn->next = insertAfter->next;
        insertAfter->next = shmNewLockRgn;
    }
    else
    {
        TRACE("adding lock into the head of the list\n");
        newLockRgn->next = fileLocks->fileLockedRgns;
        fileLocks->fileLockedRgns = shmNewLockRgn;
    }

EXIT:

    if (NO_ERROR != palError && SHMNULL != shmNewLockRgn)
    {
        SHMfree(shmNewLockRgn);
    }
   
    SHMRelease();
    
    return palError;
}

void
FILECleanUpLockedRgn(
    SHMPTR shmFileLocks,
    DWORD dwAccessRights,
    PVOID pvControllerInstance
    )
{
    SHMFILELOCKRGNS *curLockRgn = NULL, *prevLock = NULL;
    SHMFILELOCKS *fileLocks, *prevFileLocks, *nextFileLocks;
    SHMPTR shmcurLockRgn;    

    SHMLock();

    if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, fileLocks, shmFileLocks) == FALSE)
    {
        ASSERT("Unable to get pointer from shm pointer.\n");
        goto EXIT;
    }

    if (fileLocks != NULL)
    {
        if(fileLocks->fileLockedRgns !=0)
        {        
            shmcurLockRgn = fileLocks->fileLockedRgns;
            if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
            {
                ASSERT("Unable to get pointer from shm pointer.\n");
                goto EXIT;
            }
            
            while(curLockRgn != NULL)
            {
                if ((curLockRgn->pvControllerInstance == pvControllerInstance) && 
                    (curLockRgn->processId == GetCurrentProcessId()))
                {
                    /* found the locked rgn to remove from SHM */
                    TRACE("Removing the locked region (%I64u, %I64u) from SMH\n", 
                          curLockRgn->lockRgnStart, curLockRgn->nbBytesLocked);
                    
                    if (prevLock == NULL) 
                    {
                        /* removing the first lock */
                        fileLocks->fileLockedRgns = curLockRgn->next;
                        SHMfree(shmcurLockRgn);
                        shmcurLockRgn = fileLocks->fileLockedRgns;
                        if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
                        {
                            ASSERT("Unable to get pointer from shm pointer.\n");
                            goto EXIT;
                        }
                    }
                    else
                    {
                        prevLock->next = curLockRgn->next;
                        SHMfree(shmcurLockRgn);
                        shmcurLockRgn = prevLock->next;
                        if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
                        {
                            ASSERT("Unable to get pointer from shm pointer.\n");
                            goto EXIT;
                        }
                    }
                    continue;
                }
                
                prevLock = curLockRgn;
                shmcurLockRgn = curLockRgn->next;
                if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
                {
                    ASSERT("Unable to get pointer from shm pointer.\n");
                    goto EXIT;
                }
            }
        }
    
        if (dwAccessRights & GENERIC_READ)
        {   
            fileLocks->nbReadAccess--;      
        }
        if (dwAccessRights & GENERIC_WRITE)
        {
            fileLocks->nbWriteAccess--; 
        }  

        /* remove the SHMFILELOCKS structure from SHM if there's no more locked 
           region left and no more reference to it */        
        if ((--(fileLocks->refCount) == 0) && (fileLocks->fileLockedRgns == 0))
        {            
            TRACE("Removing the SHMFILELOCKS structure from SHM\n");

            if ( (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, prevFileLocks, fileLocks->prev) == FALSE) ||
                 (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKS, nextFileLocks, fileLocks->next) == FALSE))
            {
                ASSERT("Unable to get pointer from shm pointer.\n");
                goto EXIT;
            }

            if (prevFileLocks == NULL) 
            {
                /* removing the first lock file*/
                SHMSetInfo(SIID_FILE_LOCKS, fileLocks->next);
            }
            else
            {
                prevFileLocks->next = fileLocks->next;
            }

            if (nextFileLocks != NULL)
            {
                nextFileLocks->prev = fileLocks->prev;
            }

            if (fileLocks->unix_filename)
                SHMfree(fileLocks->unix_filename);

            SHMfree(shmFileLocks);
        }
    }    
EXIT:
    SHMRelease();
    return;
}

