blob: 8872ef1900e23999f991fa2266945731f555c77d [file]
//
// 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;
}