/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * locking.c * * Abstract: * * Likewise Posix File System Driver (PVFS) * * Internal Lock Manager * * Authors: Gerald Carter */ #include "pvfs.h" /* Forward declarations */ static NTSTATUS CanLock( PPVFS_LOCK_TABLE pLockTable, ULONG Key, LONG64 Offset, LONG64 Length, BOOLEAN bExclusive, BOOLEAN bSelf ); static NTSTATUS AddLock( PPVFS_CCB pCcb, ULONG Key, LONG64 Offset, LONG64 Length, BOOLEAN bExclusive ); static NTSTATUS StoreLock( PPVFS_LOCK_TABLE pLockTable, ULONG Key, LONG64 Offset, LONG64 Length, BOOLEAN bExclusive ); static VOID InitLockEntry( OUT PPVFS_LOCK_ENTRY pEntry, IN ULONG Key, IN LONG64 Offset, IN LONG64 Length, IN PVFS_LOCK_FLAGS Flags ); static NTSTATUS PvfsAddPendingLock( PPVFS_FCB pFcb, PPVFS_IRP_CONTEXT pIrpCtx, PPVFS_CCB pCcb, PPVFS_LOCK_ENTRY pLock ); static VOID PvfsProcessPendingLocks( PPVFS_FCB pFcb ); /* Code */ /************************************************************** *************************************************************/ NTSTATUS PvfsLockFile( PPVFS_IRP_CONTEXT pIrpCtx, PPVFS_CCB pCcb, ULONG Key, LONG64 Offset, LONG64 Length, PVFS_LOCK_FLAGS Flags ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PLW_LIST_LINKS pCursor = NULL; PPVFS_FCB pFcb = pCcb->pFcb; BOOLEAN bExclusive = FALSE; BOOLEAN bFcbReadLocked = FALSE; BOOLEAN bBrlWriteLocked = FALSE; PVFS_LOCK_ENTRY RangeLock = {0}; PPVFS_CCB pCurrentCcb = NULL; /* Negative locks cannot cross the 0 offset boundary */ if ((Offset < 0) && (Length != 0) && ((Offset + Length - 1) >= 0)) { ntError = STATUS_INVALID_LOCK_RANGE; BAIL_ON_NT_STATUS(ntError); } if (Flags & PVFS_LOCK_EXCLUSIVE) { bExclusive = TRUE; } /* Read lock so no one can add a CCB to the list */ LWIO_LOCK_RWMUTEX_SHARED(bFcbReadLocked, &pFcb->rwCcbLock); LWIO_LOCK_RWMUTEX_EXCLUSIVE(bBrlWriteLocked, &pFcb->rwBrlLock); while ((pCursor = PvfsListTraverse(pFcb->pCcbList, pCursor)) != NULL) { pCurrentCcb = LW_STRUCT_FROM_FIELD( pCursor, PVFS_CCB, FcbList); /* We'll deal with ourselves in AddLock() */ if (pCcb == pCurrentCcb) { continue; } ntError = CanLock( &pCurrentCcb->LockTable, Key, Offset, Length, bExclusive, FALSE); BAIL_ON_NT_STATUS(ntError); pCurrentCcb = NULL; } ntError = AddLock(pCcb, Key, Offset, Length, bExclusive); BAIL_ON_NT_STATUS(ntError); ntError = STATUS_SUCCESS; cleanup: LWIO_UNLOCK_RWMUTEX(bBrlWriteLocked, &pFcb->rwBrlLock); LWIO_UNLOCK_RWMUTEX(bFcbReadLocked, &pFcb->rwCcbLock); return ntError; error: LWIO_LOCK_RWMUTEX_EXCLUSIVE(bBrlWriteLocked, &pFcb->rwBrlLock); InitLockEntry(&RangeLock, Key, Offset, Length, Flags); /* Only try to pend a blocking lock that failed due to a conflict. Not for general failures */ if ((Flags & PVFS_LOCK_BLOCK) && (ntError == STATUS_LOCK_NOT_GRANTED)) { NTSTATUS ntErrorPending = STATUS_UNSUCCESSFUL; ntErrorPending = PvfsAddPendingLock(pFcb, pIrpCtx, pCcb, &RangeLock); if (ntErrorPending == STATUS_PENDING) { ntError = STATUS_PENDING; } } LWIO_UNLOCK_RWMUTEX(bBrlWriteLocked, &pFcb->rwBrlLock); goto cleanup; } /* Caller must hold the mutex on the FCB */ static NTSTATUS PvfsAddPendingLock( PPVFS_FCB pFcb, PPVFS_IRP_CONTEXT pIrpCtx, PPVFS_CCB pCcb, PPVFS_LOCK_ENTRY pLock ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PPVFS_PENDING_LOCK pPendingLock = NULL; /* Look for a cancellation request before re-queuing the request */ ntError = PvfsQueueCancelIrpIfRequested(pIrpCtx); BAIL_ON_NT_STATUS(ntError); ntError = PvfsAllocateMemory((PVOID*)&pPendingLock, sizeof(PVFS_PENDING_LOCK)); BAIL_ON_NT_STATUS(ntError); pPendingLock->pIrpContext = PvfsReferenceIrpContext(pIrpCtx); pPendingLock->pCcb = PvfsReferenceCCB(pCcb); pPendingLock->PendingLock = *pLock; /* structure assignment */ ntError = PvfsListAddTail( pFcb->pPendingLockQueue, &pPendingLock->LockList); BAIL_ON_NT_STATUS(ntError); if (!pIrpCtx->pFcb) { pIrpCtx->pFcb = PvfsReferenceFCB(pCcb->pFcb); } pIrpCtx->QueueType = PVFS_QUEUE_TYPE_PENDING_LOCK; PvfsIrpMarkPending(pIrpCtx, PvfsQueueCancelIrp, pIrpCtx); /* Allow the call to be cancelled while in the queue */ PvfsIrpContextClearFlag( pPendingLock->pIrpContext, PVFS_IRP_CTX_FLAG_ACTIVE); /* Memory has been given to the Queue */ pPendingLock = NULL; /* Whether we pending the IRP this time or it was previously marked as pending, we are back on the queue */ ntError = STATUS_PENDING; cleanup: PvfsFreePendingLock(&pPendingLock); return ntError; error: goto cleanup; } /************************************************************** *************************************************************/ NTSTATUS PvfsUnlockFile( PPVFS_CCB pCcb, BOOLEAN bUnlockAll, ULONG Key, LONG64 Offset, LONG64 Length ) { NTSTATUS ntError = STATUS_RANGE_NOT_LOCKED; PPVFS_FCB pFcb = pCcb->pFcb; PPVFS_LOCK_LIST pExclLocks = &pCcb->LockTable.ExclusiveLocks; PPVFS_LOCK_LIST pSharedLocks = &pCcb->LockTable.SharedLocks; PPVFS_LOCK_ENTRY pEntry = NULL; ULONG i = 0; BOOLEAN bBrlWriteLock = FALSE; LWIO_LOCK_RWMUTEX_EXCLUSIVE(bBrlWriteLock, &pFcb->rwBrlLock); /* If the caller wants to release all locks, just set the NumberOfLocks to 0 */ if (bUnlockAll && (Key == 0)) { pExclLocks->NumberOfLocks = 0; pSharedLocks->NumberOfLocks = 0; ntError = STATUS_SUCCESS; goto cleanup; } /* Exclusive Locks */ for (i=0; iNumberOfLocks; i++) { pEntry = &pExclLocks->pLocks[i]; /* Two success cases: 1. bUnlockAll is set and the Key ID matches 2. Match a single lock */ if (bUnlockAll) { if (Key == pEntry->Key) { if (((pExclLocks->NumberOfLocks-i) - 1) > 0) { RtlMoveMemory( pEntry, pEntry+1, sizeof(PVFS_LOCK_ENTRY)* ((pExclLocks->NumberOfLocks-i)-1)); } pExclLocks->NumberOfLocks--; ntError = STATUS_SUCCESS; } continue; } else if ((Offset == pEntry->Offset) && (Length == pEntry->Length) && (Key == pEntry->Key)) { if (((pExclLocks->NumberOfLocks-i) - 1) > 0) { RtlMoveMemory( pEntry, pEntry+1, sizeof(PVFS_LOCK_ENTRY)* ((pExclLocks->NumberOfLocks-i)-1)); } pExclLocks->NumberOfLocks--; ntError = STATUS_SUCCESS; goto cleanup; } } /* Shared Locks */ for (i=0; iNumberOfLocks; i++) { pEntry = &pSharedLocks->pLocks[i]; /* Two success cases: 1. bUnlockAll is set and the Key ID matches 2. Match a single lock */ if (bUnlockAll) { if (Key == pEntry->Key) { if (((pSharedLocks->NumberOfLocks-i) - 1) > 0) { RtlMoveMemory(pEntry, pEntry+1, sizeof(PVFS_LOCK_ENTRY)* ((pSharedLocks->NumberOfLocks-i)-1)); } pSharedLocks->NumberOfLocks--; ntError = STATUS_SUCCESS; } continue; } else if ((Offset == pEntry->Offset) && (Length == pEntry->Length) && (Key == pEntry->Key)) { if (((pSharedLocks->NumberOfLocks-i) - 1) > 0) { RtlMoveMemory(pEntry, pEntry+1, sizeof(PVFS_LOCK_ENTRY)* ((pSharedLocks->NumberOfLocks-i)-1)); } pSharedLocks->NumberOfLocks--; ntError = STATUS_SUCCESS; goto cleanup; } } cleanup: LWIO_UNLOCK_RWMUTEX(bBrlWriteLock, &pFcb->rwBrlLock); /* See if any pending locks can be granted now */ if (ntError == STATUS_SUCCESS) { PvfsProcessPendingLocks(pFcb); } return ntError; } /************************************************************** *************************************************************/ static VOID PvfsProcessPendingLocks( PPVFS_FCB pFcb ) { NTSTATUS ntError = STATUS_SUCCESS; PPVFS_LIST pProcessingQueue = NULL; PPVFS_PENDING_LOCK pPendingLock = NULL; PLW_LIST_LINKS pData = NULL; LONG64 ByteOffset = 0; LONG64 Length = 0; PVFS_LOCK_FLAGS Flags = 0; ULONG Key = 0; PIRP pIrp = NULL; PPVFS_CCB pCcb = NULL; BOOLEAN bBrlWriteLocked = FALSE; BOOLEAN bActive = FALSE; /* Take the pending lock queue for processing */ LWIO_LOCK_RWMUTEX_EXCLUSIVE(bBrlWriteLocked, &pFcb->rwBrlLock); if (!PvfsListIsEmpty(pFcb->pPendingLockQueue)) { pProcessingQueue = pFcb->pPendingLockQueue; pFcb->pPendingLockQueue = NULL; ntError = PvfsListInit( &pFcb->pPendingLockQueue, PVFS_FCB_MAX_PENDING_LOCKS, (PPVFS_LIST_FREE_DATA_FN)PvfsFreePendingLock); } LWIO_UNLOCK_RWMUTEX(bBrlWriteLocked, &pFcb->rwBrlLock); BAIL_ON_NT_STATUS(ntError); if (pProcessingQueue == NULL) { goto cleanup; } /* Process the pending lock queue */ while (!PvfsListIsEmpty(pProcessingQueue)) { pData = NULL; pPendingLock = NULL; ntError = PvfsListRemoveHead(pProcessingQueue, &pData); BAIL_ON_NT_STATUS(ntError); pPendingLock = LW_STRUCT_FROM_FIELD( pData, PVFS_PENDING_LOCK, LockList); pCcb = pPendingLock->pCcb; pIrp = pPendingLock->pIrpContext->pIrp; PvfsQueueCancelIrpIfRequested(pPendingLock->pIrpContext); bActive = PvfsIrpContextMarkIfNotSetFlag( pPendingLock->pIrpContext, PVFS_IRP_CTX_FLAG_CANCELLED, PVFS_IRP_CTX_FLAG_ACTIVE); if (!bActive) { PvfsFreePendingLock(&pPendingLock); continue; } ByteOffset = pPendingLock->PendingLock.Offset; Length = pPendingLock->PendingLock.Length; Key = pPendingLock->PendingLock.Key; Flags = PVFS_LOCK_BLOCK; if (pPendingLock->PendingLock.bExclusive) { Flags |= PVFS_LOCK_EXCLUSIVE; } /* If the lock still cannot be granted, this will add it back to the FCB's pending lock queue. We don't need to lock the IrpCtx here since it is too late to cancel and we are not in the worker thread queue. */ ntError = PvfsLockFile(pPendingLock->pIrpContext, pCcb, Key, ByteOffset, Length, Flags); if (ntError != STATUS_PENDING) { /* We've processed the lock (to success or failure) */ pIrp->IoStatusBlock.Status = ntError; PvfsAsyncIrpComplete(pPendingLock->pIrpContext); } /* If the lock was pended again, it ahs a new record, so free this one. But avoid Freeing the IrpContext here. This should probably be using a ref count. But because of the relationship between completing an Irp and an IrpContext, we do not. */ PvfsFreePendingLock(&pPendingLock); } cleanup: if (pProcessingQueue) { PvfsListDestroy(&pProcessingQueue); } return; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static BOOLEAN DoRangesOverlap( LONG64 Offset1, LONG64 Length1, LONG64 Offset2, LONG64 Length2 ) { LONG64 Start1, Start2, End1, End2; /* Zero byte locks form a boundary that overlaps when crossed */ if ((Length1 == 0) && (Length2 == 0)) { /* Two Zero byte locks never conflict with each other */ return FALSE; } else if (Length1 == 0) { Start2 = Offset2; End2 = Offset2 + Length2 - 1; return ((Offset1 > Start2) && (Offset1 <= End2)) ? TRUE : FALSE; } else if (Length2 == 0) { Start1 = Offset1; End1 = Offset1 + Length1 - 1; return ((Offset2 > Start1) && (Offset2 <= End1)) ? TRUE : FALSE; } else { Start1 = Offset1; End1 = Offset1 + Length1 - 1; Start2 = Offset2; End2 = Offset2 + Length2 - 1; if (((Start1 >= Start2) && (Start1 <= End2)) || ((End1 >= Start2) && (End1 <= End2))) { return TRUE; } } return FALSE; } static NTSTATUS CanLock( PPVFS_LOCK_TABLE pLockTable, ULONG Key, LONG64 Offset, LONG64 Length, BOOLEAN bExclusive, BOOLEAN bSelf ) { NTSTATUS ntError = STATUS_SUCCESS; ULONG i = 0; PPVFS_LOCK_LIST pExclLocks = &pLockTable->ExclusiveLocks; PPVFS_LOCK_LIST pSharedLocks = &pLockTable->SharedLocks; PPVFS_LOCK_ENTRY pEntry = NULL; /* Trivial case */ if ((pExclLocks->NumberOfLocks == 0) && (pSharedLocks->NumberOfLocks == 0)) { ntError = STATUS_SUCCESS; goto cleanup; } /* Exclusive Locks */ for (i=0; iNumberOfLocks; i++) { pEntry = &pExclLocks->pLocks[i]; if (DoRangesOverlap(Offset, Length, pEntry->Offset, pEntry->Length)) { if (bExclusive || !(bSelf && (Key == pEntry->Key))) { ntError = STATUS_LOCK_NOT_GRANTED; BAIL_ON_NT_STATUS(ntError); } } } /* Shared Locks */ /* Fast path. No Exclusive locks */ if ((pExclLocks->NumberOfLocks == 0) && (!bExclusive)) { ntError = STATUS_SUCCESS; goto cleanup; } for (i=0; iNumberOfLocks; i++) { pEntry = &pSharedLocks->pLocks[i]; if (DoRangesOverlap(Offset, Length, pEntry->Offset, pEntry->Length)) { if (bExclusive) { ntError = STATUS_LOCK_NOT_GRANTED; BAIL_ON_NT_STATUS(ntError); } } } ntError = STATUS_SUCCESS; cleanup: return ntError; error: if ((ntError == STATUS_LOCK_NOT_GRANTED) && (Offset >= 0xEF000000)) { ntError = STATUS_FILE_LOCK_CONFLICT; } goto cleanup; } /************************************************************** *************************************************************/ static NTSTATUS AddLock( PPVFS_CCB pCcb, ULONG Key, LONG64 Offset, LONG64 Length, BOOLEAN bExclusive ) { NTSTATUS ntError = STATUS_SUCCESS; PPVFS_LOCK_TABLE pLockTable = &pCcb->LockTable; ntError = CanLock(pLockTable, Key, Offset, Length, bExclusive, TRUE); BAIL_ON_NT_STATUS(ntError); ntError = StoreLock(pLockTable, Key, Offset, Length, bExclusive); BAIL_ON_NT_STATUS(ntError); ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; } /************************************************************** *************************************************************/ static NTSTATUS StoreLock( PPVFS_LOCK_TABLE pLockTable, ULONG Key, LONG64 Offset, LONG64 Length, BOOLEAN bExclusive ) { NTSTATUS ntError= STATUS_SUCCESS; ULONG NewListSize = 0; PPVFS_LOCK_ENTRY pLocks = NULL; ULONG NumLocks = 0; PPVFS_LOCK_LIST pList = NULL; if (bExclusive) { pList = &pLockTable->ExclusiveLocks; } else { pList = &pLockTable->SharedLocks; } if (pList->NumberOfLocks == pList->ListSize) { NewListSize = pList->ListSize + 64; ntError = PvfsReallocateMemory((PVOID*)&pList->pLocks, sizeof(PVFS_LOCK_ENTRY)*NewListSize); BAIL_ON_NT_STATUS(ntError); pList->ListSize = NewListSize; } pLocks = pList->pLocks; NumLocks = pList->NumberOfLocks; pLocks[NumLocks].bExclusive = bExclusive; pLocks[NumLocks].Offset = Offset; pLocks[NumLocks].Length = Length; pLocks[NumLocks].Key = Key; NumLocks++; pList->NumberOfLocks = NumLocks; ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; } /************************************************************** *************************************************************/ static VOID InitLockEntry( OUT PPVFS_LOCK_ENTRY pEntry, IN ULONG Key, IN LONG64 Offset, IN LONG64 Length, IN PVFS_LOCK_FLAGS Flags ) { /* Should never happen, but don't crash if it does */ if (pEntry == NULL) { return; } pEntry->bFailImmediately = (Flags & PVFS_LOCK_BLOCK) ? FALSE : TRUE; pEntry->bExclusive = (Flags & PVFS_LOCK_EXCLUSIVE) ? TRUE : FALSE; pEntry->Key = Key; pEntry->Offset = Offset; pEntry->Length = Length; return; } /************************************************************** *************************************************************/ static NTSTATUS PvfsCheckLockedRegionCanRead( IN PPVFS_CCB pCcb, IN BOOLEAN bSelf, IN ULONG Key, IN LONG64 Offset, IN ULONG Length ); static NTSTATUS PvfsCheckLockedRegionCanWrite( IN PPVFS_CCB pCcb, IN BOOLEAN bSelf, IN ULONG Key, IN LONG64 Offset, IN ULONG Length ); NTSTATUS PvfsCheckLockedRegion( IN PPVFS_CCB pCcb, IN PVFS_OPERATION_TYPE Operation, IN ULONG Key, IN LONG64 Offset, IN ULONG Length ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PLW_LIST_LINKS pCursor = NULL; PPVFS_FCB pFcb = pCcb->pFcb; BOOLEAN bFcbLocked = FALSE; BOOLEAN bBrlLocked = FALSE; PPVFS_CCB pCurrentCcb = NULL; /* Sanity checks */ BAIL_ON_INVALID_PTR(pFcb, ntError); switch(Operation) { case PVFS_OPERATION_READ: case PVFS_OPERATION_WRITE: ntError = STATUS_SUCCESS; break; default: ntError = STATUS_INVALID_PARAMETER; break; } BAIL_ON_NT_STATUS(ntError); /* Zero byte reads and writes don't conflict */ if (Length == 0) { ntError = STATUS_SUCCESS; goto cleanup; } /* Read locks so no one can add a CCB to the list, or add a new BRL. */ LWIO_LOCK_RWMUTEX_SHARED(bFcbLocked, &pFcb->rwCcbLock); LWIO_LOCK_RWMUTEX_SHARED(bBrlLocked, &pFcb->rwBrlLock); while ((pCursor = PvfsListTraverse(pFcb->pCcbList, pCursor))!= NULL) { pCurrentCcb = LW_STRUCT_FROM_FIELD( pCursor, PVFS_CCB, FcbList); switch(Operation) { case PVFS_OPERATION_READ: ntError = PvfsCheckLockedRegionCanRead( pCurrentCcb, (pCcb == pCurrentCcb) ? TRUE : FALSE, Key, Offset, Length); break; case PVFS_OPERATION_WRITE: ntError = PvfsCheckLockedRegionCanWrite( pCurrentCcb, (pCcb == pCurrentCcb) ? TRUE : FALSE, Key, Offset, Length); break; } BAIL_ON_NT_STATUS(ntError); pCurrentCcb = NULL; } cleanup: LWIO_UNLOCK_RWMUTEX(bBrlLocked, &pFcb->rwBrlLock); LWIO_UNLOCK_RWMUTEX(bFcbLocked, &pFcb->rwCcbLock); return ntError; error: goto cleanup; } /************************************************************** *************************************************************/ static NTSTATUS PvfsCheckLockedRegionCanRead( IN PPVFS_CCB pCcb, IN BOOLEAN bSelf, IN ULONG Key, IN LONG64 Offset, IN ULONG Length ) { NTSTATUS ntError = STATUS_SUCCESS; ULONG i = 0; PPVFS_LOCK_LIST pExclLocks = &pCcb->LockTable.ExclusiveLocks; PPVFS_LOCK_ENTRY pEntry = NULL; /* Trivial case */ if (pExclLocks->NumberOfLocks == 0) { ntError = STATUS_SUCCESS; goto cleanup; } /* Check Lock table */ for (i=0; iNumberOfLocks; i++) { pEntry = &pExclLocks->pLocks[i]; if (DoRangesOverlap(Offset, Length, pEntry->Offset, pEntry->Length)) { if (!(bSelf && (Key == pEntry->Key))) { ntError = STATUS_FILE_LOCK_CONFLICT; BAIL_ON_NT_STATUS(ntError); } } } cleanup: return ntError; error: goto cleanup; } /************************************************************** *************************************************************/ static NTSTATUS PvfsCheckLockedRegionCanWrite( IN PPVFS_CCB pCcb, IN BOOLEAN bSelf, IN ULONG Key, IN LONG64 Offset, IN ULONG Length ) { NTSTATUS ntError = STATUS_SUCCESS; ULONG i = 0; PPVFS_LOCK_LIST pExclLocks = &pCcb->LockTable.ExclusiveLocks; PPVFS_LOCK_LIST pSharedLocks = &pCcb->LockTable.SharedLocks; PPVFS_LOCK_ENTRY pEntry = NULL; /* A Shared lock prevents all write access to a region...even ourself */ for (i=0; iNumberOfLocks; i++) { pEntry = &pSharedLocks->pLocks[i]; if (DoRangesOverlap(Offset, Length, pEntry->Offset, pEntry->Length)) { ntError = STATUS_FILE_LOCK_CONFLICT; BAIL_ON_NT_STATUS(ntError); } } /* Region must be unlocked */ for (i=0; iNumberOfLocks; i++) { pEntry = &pExclLocks->pLocks[i]; if (DoRangesOverlap(Offset, Length, pEntry->Offset, pEntry->Length)) { if (!(bSelf && (Key == pEntry->Key))) { ntError = STATUS_FILE_LOCK_CONFLICT; BAIL_ON_NT_STATUS(ntError); } } } cleanup: return ntError; error: goto cleanup; } /***************************************************************************** ****************************************************************************/ NTSTATUS PvfsCreateLockContext( OUT PPVFS_PENDING_LOCK *ppLockContext, IN PPVFS_IRP_CONTEXT pIrpContext, IN PPVFS_CCB pCcb, IN ULONG Key, IN LONG64 Offset, IN LONG64 Length, IN PVFS_LOCK_FLAGS Flags ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PPVFS_PENDING_LOCK pLockCtx; ntError = PvfsAllocateMemory((PVOID*)&pLockCtx, sizeof(PVFS_PENDING_LOCK)); BAIL_ON_NT_STATUS(ntError); pLockCtx->pIrpContext = PvfsReferenceIrpContext(pIrpContext); pLockCtx->pCcb = PvfsReferenceCCB(pCcb); InitLockEntry(&pLockCtx->PendingLock, Key, Offset, Length, Flags); *ppLockContext = pLockCtx; pLockCtx = NULL; cleanup: return ntError; error: goto cleanup; } /***************************************************************************** ****************************************************************************/ VOID PvfsFreeLockContext( IN OUT PVOID *ppContext ) { PPVFS_PENDING_LOCK pLockCtx = NULL; if (ppContext && *ppContext) { pLockCtx = (PPVFS_PENDING_LOCK)(*ppContext); if (pLockCtx->pIrpContext) { PvfsReleaseIrpContext(&pLockCtx->pIrpContext); } if (pLockCtx->pCcb) { PvfsReleaseCCB(pLockCtx->pCcb); } PVFS_FREE(ppContext); } return; } /***************************************************************************** ****************************************************************************/ NTSTATUS PvfsLockFileWithContext( PVOID pContext ) { PPVFS_PENDING_LOCK pLockCtx = (PPVFS_PENDING_LOCK)pContext; PVFS_LOCK_FLAGS Flags = 0; if (pLockCtx->PendingLock.bExclusive) { Flags |= PVFS_LOCK_EXCLUSIVE; } if (!pLockCtx->PendingLock.bFailImmediately) { Flags |= PVFS_LOCK_BLOCK; } return PvfsLockFile( pLockCtx->pIrpContext, pLockCtx->pCcb, pLockCtx->PendingLock.Key, pLockCtx->PendingLock.Offset, pLockCtx->PendingLock.Length, Flags); } /***************************************************************************** ****************************************************************************/ static BOOLEAN PvfsHandleHasOpenByteRangeLocks( PPVFS_CCB pCcb ) { BOOLEAN bHasLocks = FALSE; if ((pCcb->LockTable.ExclusiveLocks.NumberOfLocks != 0) || (pCcb->LockTable.SharedLocks.NumberOfLocks != 0)) { bHasLocks = TRUE; } return bHasLocks; } BOOLEAN PvfsFileHasOpenByteRangeLocks( PPVFS_FCB pFcb ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PLW_LIST_LINKS pCursor = NULL; BOOLEAN bFcbLocked = FALSE; BOOLEAN bBrlLocked = FALSE; BOOLEAN bFileIsLocked = FALSE; PPVFS_CCB pCurrentCcb = NULL; /* Sanity checks */ BAIL_ON_INVALID_PTR(pFcb, ntError); /* Read locks so no one can add a CCB to the list, or add a new BRL. */ LWIO_LOCK_RWMUTEX_SHARED(bFcbLocked, &pFcb->rwCcbLock); LWIO_LOCK_RWMUTEX_SHARED(bBrlLocked, &pFcb->rwBrlLock); while ((pCursor = PvfsListTraverse(pFcb->pCcbList, pCursor)) != NULL) { pCurrentCcb = LW_STRUCT_FROM_FIELD( pCursor, PVFS_CCB, FcbList); if (PvfsHandleHasOpenByteRangeLocks(pCurrentCcb)) { bFileIsLocked = TRUE; break; } pCurrentCcb = NULL; } cleanup: LWIO_UNLOCK_RWMUTEX(bBrlLocked, &pFcb->rwBrlLock); LWIO_UNLOCK_RWMUTEX(bFcbLocked, &pFcb->rwCcbLock); return bFileIsLocked; error: goto cleanup; } /***************************************************************************** ****************************************************************************/ static NTSTATUS PvfsCleanPendingLockQueue( PVOID pContext ); static VOID PvfsCleanPendingLockFree( PVOID *ppContext ); NTSTATUS PvfsScheduleCancelLock( PPVFS_IRP_CONTEXT pIrpContext ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PPVFS_WORK_CONTEXT pWorkCtx = NULL; PPVFS_IRP_CONTEXT pIrpCtx = NULL; BAIL_ON_INVALID_PTR(pIrpContext->pFcb, ntError); pIrpCtx = PvfsReferenceIrpContext(pIrpContext); ntError = PvfsCreateWorkContext( &pWorkCtx, FALSE, pIrpContext, (PPVFS_WORK_CONTEXT_CALLBACK)PvfsCleanPendingLockQueue, (PPVFS_WORK_CONTEXT_FREE_CTX)PvfsCleanPendingLockFree); BAIL_ON_NT_STATUS(ntError); ntError = PvfsAddWorkItem(gpPvfsInternalWorkQueue, (PVOID)pWorkCtx); BAIL_ON_NT_STATUS(ntError); pWorkCtx = NULL; cleanup: PVFS_FREE(&pWorkCtx); return ntError; error: if (pIrpCtx) { PvfsReleaseIrpContext(&pIrpCtx); } goto cleanup; } /***************************************************************************** ****************************************************************************/ static NTSTATUS PvfsCleanPendingLockQueue( PVOID pContext ) { NTSTATUS ntError = STATUS_SUCCESS; PPVFS_IRP_CONTEXT pIrpContext = (PPVFS_IRP_CONTEXT)pContext; PPVFS_FCB pFcb = PvfsReferenceFCB(pIrpContext->pFcb); BOOLEAN bLocked = FALSE; PPVFS_PENDING_LOCK pLockRecord = NULL; PLW_LIST_LINKS pLockRecordLink = NULL; PLW_LIST_LINKS pNextLink = NULL; BOOLEAN bFound = FALSE; LWIO_LOCK_RWMUTEX_EXCLUSIVE(bLocked, &pFcb->rwBrlLock); pLockRecordLink = PvfsListTraverse(pFcb->pPendingLockQueue, NULL); while (pLockRecordLink) { pLockRecord = LW_STRUCT_FROM_FIELD( pLockRecordLink, PVFS_PENDING_LOCK, LockList); pNextLink = PvfsListTraverse(pFcb->pPendingLockQueue, pLockRecordLink); if (pLockRecord->pIrpContext != pIrpContext) { pLockRecordLink = pNextLink; continue; } bFound = TRUE; PvfsListRemoveItem(pFcb->pPendingLockQueue, pLockRecordLink); pLockRecordLink = NULL; pLockRecord->pIrpContext->pIrp->IoStatusBlock.Status = STATUS_CANCELLED; PvfsAsyncIrpComplete(pLockRecord->pIrpContext); PvfsFreePendingLock(&pLockRecord); /* Can only be one IrpContext match so we are done */ } LWIO_UNLOCK_RWMUTEX(bLocked, &pFcb->rwBrlLock); if (!bFound) { pIrpContext->pIrp->IoStatusBlock.Status = STATUS_CANCELLED; PvfsAsyncIrpComplete(pIrpContext); } if (pFcb) { PvfsReleaseFCB(&pFcb); } if (pIrpContext) { PvfsReleaseIrpContext(&pIrpContext); } return ntError; } /***************************************************************************** ****************************************************************************/ static VOID PvfsCleanPendingLockFree( PVOID *ppContext ) { /* No op -- context released in PvfsOplockCleanPendingLockQueue */ return; } /***************************************************************************** ****************************************************************************/ VOID PvfsFreePendingLock( PPVFS_PENDING_LOCK *ppPendingLock ) { PPVFS_PENDING_LOCK pLock = NULL; if (ppPendingLock && *ppPendingLock) { pLock = *ppPendingLock; if (pLock->pIrpContext) { PvfsReleaseIrpContext(&pLock->pIrpContext); } if (pLock->pCcb) { PvfsReleaseCCB(pLock->pCcb); } PVFS_FREE(ppPendingLock); } return; } /* local variables: mode: c c-basic-offset: 4 indent-tabs-mode: nil tab-width: 4 end: */