/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * 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
 * <http://www.gnu.org/licenses/>.
 *
 * 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:
 *
 *       lease.c
 *
 * Abstract:
 *
 *        Likewise Posix File System Driver (PVFS)
 *
 *        lease Package
 *
 * Authors: Gerald Carter <gcarter@likewise.com>
 */

#include "pvfs.h"

static
BOOLEAN
PvfsIsSameLeaseKey(
    IN PPVFS_LEASE_KEY pLeaseKey1,
    IN PPVFS_LEASE_KEY pLeaseKey2
    );

static
BOOLEAN
PvfsLeaseIsMine(
    PPVFS_CCB pCcb,
    PPVFS_OPLOCK_RECORD pOplock
    );

static
BOOLEAN
PvfsStreamAllOtherOpensHasSameOplockKey(
    IN PPVFS_SCB pScb
    );

static
NTSTATUS
PvfsLeaseGrantRead(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    );

static
NTSTATUS
PvfsLeaseGrantReadHandle(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    );

static
NTSTATUS
PvfsLeaseGrantReadWrite(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    );

static
NTSTATUS
PvfsLeaseGrantReadWriteHandle(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    );

BOOLEAN
PvfsIsReadLease(
    IN IO_LEASE_STATE LeaseState
    )
{
    return (IsSetFlag(LeaseState, IO_LEASE_STATE_READ) &&
           !IsSetFlag(LeaseState, IO_LEASE_STATE_WRITE) &&
           !IsSetFlag(LeaseState, IO_LEASE_STATE_HANDLE));
}

BOOLEAN
PvfsIsReadHandleLease(
    IN IO_LEASE_STATE LeaseState
    )
{
    return (IsSetFlag(LeaseState, IO_LEASE_STATE_READ) &&
           !IsSetFlag(LeaseState, IO_LEASE_STATE_WRITE) &&
           IsSetFlag(LeaseState, IO_LEASE_STATE_HANDLE));
}

BOOLEAN
PvfsIsReadWriteLease(
    IN IO_LEASE_STATE LeaseState
    )
{
    return (IsSetFlag(LeaseState, IO_LEASE_STATE_READ) &&
            IsSetFlag(LeaseState, IO_LEASE_STATE_WRITE) &&
           !IsSetFlag(LeaseState, IO_LEASE_STATE_HANDLE));
}

BOOLEAN
PvfsIsReadWriteHandleLease(
    IN IO_LEASE_STATE LeaseState
    )
{
    return (IsSetFlag(LeaseState, IO_LEASE_STATE_READ) &&
            IsSetFlag(LeaseState, IO_LEASE_STATE_WRITE) &&
            IsSetFlag(LeaseState, IO_LEASE_STATE_HANDLE));
}

BOOLEAN
PvfsIsValidLeaseState(
    IN IO_LEASE_STATE LeaseState
    )
{
    return PvfsIsReadLease(LeaseState) ||
           PvfsIsReadHandleLease(LeaseState) ||
           PvfsIsReadWriteLease(LeaseState) ||
           PvfsIsReadWriteHandleLease(LeaseState);
}

////////////////////////////////////////////////////////////////////////

NTSTATUS
PvfsOplockGrantLease(
    IN PPVFS_IRP_CONTEXT pIrpContext,
    IN PPVFS_CCB pCcb,
    IN IO_LEASE_STATE LeaseState,
    IN IO_LEASE_FLAG Flags
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    IO_LEASE_STATE currentLease = IO_LEASE_STATE_NONE;

    // If this is an ACK, then we have previous state to adjust
    // Can only receive an ACK on the handle that initiated
    // the BREAK.

    if (IsSetFlag(Flags, IO_LEASE_FLAG_ACK))
    {
        BOOLEAN ccbLocked = FALSE;

        LWIO_LOCK_MUTEX(ccbLocked, &pCcb->ControlBlock);
        if (pCcb->OplockState == PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS)
        {
            pCcb->OplockState = PVFS_OPLOCK_STATE_NONE;
        }
        else
        {
            ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
        }
        LWIO_UNLOCK_MUTEX(ccbLocked, &pCcb->ControlBlock);

        BAIL_ON_NT_STATUS(ntError);

        ntError = PvfsOplockMarkPendedOpsReady(pCcb->pScb);
        BAIL_ON_NT_STATUS(ntError);
    }

    LWIO_ASSERT(pCcb->LeaseKey != NULL);

    currentLease = PvfsLeaseKeyGetLeaseState(pCcb->LeaseKey);

    // Disallow downgrades

    if ((currentLease != IO_LEASE_STATE_NONE) &&
        ((currentLease & LeaseState) != currentLease))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    ntError = STATUS_OPLOCK_NOT_GRANTED;

    switch (LeaseState)
    {
        case IO_LEASE_STATE_READ:
            ntError = PvfsLeaseGrantRead(pIrpContext, pCcb);
            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
            ntError = PvfsLeaseGrantReadHandle(pIrpContext, pCcb);
            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:
            ntError = PvfsLeaseGrantReadWrite(pIrpContext, pCcb);
            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:
            ntError = PvfsLeaseGrantReadWriteHandle(pIrpContext, pCcb);
            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
            break;
    }
    BAIL_ON_NT_STATUS(ntError);

error:

    return ntError;
}

////////////////////////////////////////////////////////////////////////

static
NTSTATUS
PvfsLeaseGrantRead(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    )
{
    NTSTATUS ntError = STATUS_OPLOCK_NOT_GRANTED;
    PPVFS_SCB pScb = NULL;
    BOOLEAN bScbLocked = FALSE;
    PPVFS_OPLOCK_RECORD pOplock = NULL;
    PLW_LIST_LINKS pOplockLink = NULL;
    PPVFS_OPLOCK_RECORD leaseToUpgrade = NULL;

    BAIL_ON_INVALID_PTR(pCcb->pScb, ntError);

    pScb = pCcb->pScb;

    LWIO_LOCK_MUTEX(bScbLocked, &pScb->BaseControlBlock.Mutex);

    // Cannot grant a read lease on an existing exclusive oplock or RW or RWH - FAIL

    if (PvfsStreamIsOplockedExclusive(pScb) ||
        PvfsStreamIsLeaseReadWrite(pScb) ||
        PvfsStreamIsLeaseReadWriteHandle(pScb))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    /* Cannot grant a read lease if there are any open byte range
       locks on the file */

    if (PvfsStreamHasOpenByteRangeLocks(pScb))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    // Go through existing OplockList to see whether we generate
    // 'STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE' (for level 2 and read) Or
    // STATUS_OPLOCK_NOT_GRANTED

    while ((pOplockLink = PvfsListTraverse(pScb->pOplockList, pOplockLink)) != NULL)
    {
        pOplock = LW_STRUCT_FROM_FIELD(
                          pOplockLink,
                          PVFS_OPLOCK_RECORD,
                          OplockList);

        if (PvfsIrpContextCheckFlag(
                    pOplock->pIrpContext,
                    PVFS_IRP_CTX_FLAG_CANCELLED))
        {
            pOplock = NULL;
            continue;
        }

        // Current lease is level 2 / R && Requesting R
        if (pOplock->OplockType == IO_OPLOCK_REQUEST_OPLOCK_LEVEL_2 ||
            (pOplock->OplockType == IO_OPLOCK_REQUEST_LEASE &&
             PvfsIsReadLease(pOplock->LeaseState)))
        {
            if (PvfsIsSameLeaseKey(pOplock->pCcb->LeaseKey, pCcb->LeaseKey))
            {
                // Cannot ever have two different lease entries under
                // the same key
                LWIO_ASSERT(leaseToUpgrade == NULL);

                // Have to process everything before we know if we can
                // upgrade our lease
                leaseToUpgrade = pOplock;
            }
        }
        // Current lease is RH && Requesting R
        else if (pOplock->OplockType == IO_OPLOCK_REQUEST_LEASE &&
                 PvfsIsReadHandleLease(pOplock->LeaseState))
        {
            // Already checked for lease downgrade request in caller
            LWIO_ASSERT(!PvfsIsSameLeaseKey(pOplock->pCcb->LeaseKey, pCcb->LeaseKey));
        }
        else
        {
            // this should not happen as it has already been checked up front
            LWIO_ASSERT(FALSE);
        }
    }

    if (leaseToUpgrade)
    {
        // remove pOplock
        PvfsListRemoveItem(pScb->pOplockList, &leaseToUpgrade->OplockList);

        leaseToUpgrade->pIrpContext->pIrp->IoStatusBlock.Status = STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE;
        PvfsAsyncCompleteIrpContext(leaseToUpgrade->pIrpContext);

        PvfsFreeOplockRecord(&leaseToUpgrade);
    }

    /// Can have multiple level2 oplocks and/or read leases - GRANT

    ntError = PvfsAddLeaseRecord(
                  pScb,
                  pIrpContext,
                  pCcb,
                  IO_LEASE_STATE_READ);
    BAIL_ON_NT_STATUS(ntError);

    ntError = STATUS_SUCCESS;

error:

    LWIO_UNLOCK_MUTEX(bScbLocked, &pScb->BaseControlBlock.Mutex);

    return ntError;
}

////////////////////////////////////////////////////////////////////////

static
NTSTATUS
PvfsLeaseGrantReadHandle(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    )
{
    NTSTATUS ntError = STATUS_OPLOCK_NOT_GRANTED;
    PPVFS_SCB pScb = NULL;
    BOOLEAN bScbLocked = FALSE;
    PPVFS_OPLOCK_RECORD pOplock = NULL;
    PLW_LIST_LINKS pOplockLink = NULL;
    PPVFS_OPLOCK_RECORD leaseToUpgrade = NULL;

    BAIL_ON_INVALID_PTR(pCcb->pScb, ntError);

    pScb = pCcb->pScb;

    LWIO_LOCK_MUTEX(bScbLocked, &pScb->BaseControlBlock.Mutex);

    // Cannot grant a read lease on an existing exclusive oplock or
    // RW or RWH - FAIL

    if (PvfsStreamIsOplockedExclusive(pScb) ||
        PvfsStreamIsOplockedShared(pScb) ||
        PvfsStreamIsLeaseReadWrite(pScb) ||
        PvfsStreamIsLeaseReadWriteHandle(pScb))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    // Cannot grant a read lease if there are any open byte range
    // locks on the file

    if (PvfsStreamHasOpenByteRangeLocks(pScb))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    // Go through existing OplockList to see whether we generate
    // 'STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE' for read or read handle

    while ((pOplockLink = PvfsListTraverse(pScb->pOplockList, pOplockLink)) != NULL)
    {
        pOplock = LW_STRUCT_FROM_FIELD(
                          pOplockLink,
                          PVFS_OPLOCK_RECORD,
                          OplockList);

        if (PvfsIrpContextCheckFlag(
                    pOplock->pIrpContext,
                    PVFS_IRP_CTX_FLAG_CANCELLED))
        {
            pOplock = NULL;
            continue;
        }

        // Todo: In case an existing RH lease, currently treat RH same as R

        if (pOplock->OplockType == IO_OPLOCK_REQUEST_LEASE &&
            (PvfsIsReadLease(pOplock->LeaseState) ||
             PvfsIsReadHandleLease(pOplock->LeaseState)))
        {
            if (PvfsIsSameLeaseKey(pOplock->pCcb->LeaseKey, pCcb->LeaseKey))
            {
                // Cannot ever have two different lease entries under
                // the same key
                LWIO_ASSERT(leaseToUpgrade == NULL);

                // Have to process everything before we know if we can
                // upgrade our lease
                leaseToUpgrade = pOplock;
            }

            // No conflict.  Continue processing the list
        }

        // No conflict with the current oplock record.  Continue processing list
    }

    if (leaseToUpgrade)
    {
        // remove pOplock
        PvfsListRemoveItem(pScb->pOplockList, &leaseToUpgrade->OplockList);

        leaseToUpgrade->pIrpContext->pIrp->IoStatusBlock.Status =
            STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE;
        PvfsAsyncCompleteIrpContext(leaseToUpgrade->pIrpContext);

        PvfsFreeOplockRecord(&leaseToUpgrade);
    }

    // Can have multiple read and read handle leases - GRANT

    ntError = PvfsAddLeaseRecord(
                  pScb,
                  pIrpContext,
                  pCcb,
                  IO_LEASE_STATE_READ|IO_LEASE_STATE_HANDLE);
    BAIL_ON_NT_STATUS(ntError);

    ntError = STATUS_SUCCESS;

error:

    LWIO_UNLOCK_MUTEX(bScbLocked, &pScb->BaseControlBlock.Mutex);

    return ntError;
}

////////////////////////////////////////////////////////////////////////

static
NTSTATUS
PvfsLeaseGrantReadWrite(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    )
{
    NTSTATUS ntError = STATUS_UNSUCCESSFUL;
    PPVFS_SCB pScb = NULL;
    BOOLEAN bScbControlLocked = FALSE;
    PPVFS_OPLOCK_RECORD pOplock = NULL;
    PLW_LIST_LINKS pOplockLink = NULL;
    PPVFS_OPLOCK_RECORD leaseToUpgrade = NULL;

    BAIL_ON_INVALID_PTR(pCcb->pScb, ntError);

    pScb = pCcb->pScb;

    LWIO_LOCK_MUTEX(bScbControlLocked, &pScb->BaseControlBlock.Mutex);

    // Any other opens - FAIL
    // Cannot grant a second exclusive oplock - FAIL

    if ((pScb->OpenHandleCount > 1 &&
         !PvfsStreamAllOtherOpensHasSameOplockKey(pScb)) ||
        PvfsStreamIsOplockedExclusive(pScb) ||
        PvfsStreamIsOplockedShared(pScb) ||
        PvfsStreamIsLeaseReadHandle(pScb) ||
        PvfsStreamIsLeaseReadWriteHandle(pScb))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    // Go through existing OplockList to see whether we generate
    // 'STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE'

    while ((pOplockLink = PvfsListTraverse(pScb->pOplockList, pOplockLink)) != NULL)
    {
        pOplock = LW_STRUCT_FROM_FIELD(
                          pOplockLink,
                          PVFS_OPLOCK_RECORD,
                          OplockList);

        if (PvfsIrpContextCheckFlag(pOplock->pIrpContext, PVFS_IRP_CTX_FLAG_CANCELLED))
        {
            pOplock = NULL;
            continue;
        }

        if (pOplock->OplockType == IO_OPLOCK_REQUEST_LEASE &&
            (PvfsIsReadLease(pOplock->LeaseState) ||
             PvfsIsReadWriteLease(pOplock->LeaseState)))
        {
            if (PvfsLeaseIsMine(pCcb, pOplock))
            {
                // Cannot ever have two different lease entries under
                // the same key
                LWIO_ASSERT(leaseToUpgrade == NULL);

                // Have to process everything before we know if we can
                // upgrade our lease
                leaseToUpgrade = pOplock;
            }
            else
            {
                ntError = STATUS_OPLOCK_NOT_GRANTED;
                BAIL_ON_NT_STATUS(ntError);
            }
        }
        else
        {
            // this should not happen as it has already been checked upfront
            LWIO_ASSERT(FALSE);
        }
    }

    if (leaseToUpgrade)
    {
        // remove pOplock
        PvfsListRemoveItem(pScb->pOplockList, &leaseToUpgrade->OplockList);

        leaseToUpgrade->pIrpContext->pIrp->IoStatusBlock.Status = STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE;
        PvfsAsyncCompleteIrpContext(leaseToUpgrade->pIrpContext);

        PvfsFreeOplockRecord(&leaseToUpgrade);
    }

    // Grant RW lease

    ntError = PvfsAddLeaseRecord(
                  pScb,
                  pIrpContext,
                  pCcb,
                  IO_LEASE_STATE_READ|IO_LEASE_STATE_WRITE);
    BAIL_ON_NT_STATUS(ntError);

    ntError = STATUS_SUCCESS;

error:

    LWIO_UNLOCK_MUTEX(bScbControlLocked, &pScb->BaseControlBlock.Mutex);

    return ntError;
}

////////////////////////////////////////////////////////////////////////

static
NTSTATUS
PvfsLeaseGrantReadWriteHandle(
    PPVFS_IRP_CONTEXT pIrpContext,
    PPVFS_CCB pCcb
    )
{
    NTSTATUS ntError = STATUS_UNSUCCESSFUL;
    PPVFS_SCB pScb = NULL;
    BOOLEAN bScbControlLocked = FALSE;
    PPVFS_OPLOCK_RECORD pOplock = NULL;
    PLW_LIST_LINKS pOplockLink = NULL;
    PPVFS_OPLOCK_RECORD leaseToUpgrade = NULL;

    BAIL_ON_INVALID_PTR(pCcb->pScb, ntError);

    pScb = pCcb->pScb;

    LWIO_LOCK_MUTEX(bScbControlLocked, &pScb->BaseControlBlock.Mutex);

    // Any other opens - FAIL
    // Cannot grant a second exclusive oplock - FAIL

    if ((pScb->OpenHandleCount > 1 &&
         !PvfsStreamAllOtherOpensHasSameOplockKey(pScb)) ||
        PvfsStreamIsOplockedExclusive(pScb) ||
        PvfsStreamIsOplockedShared(pScb))
    {
        ntError = STATUS_OPLOCK_NOT_GRANTED;
        BAIL_ON_NT_STATUS(ntError);
    }

    // Go through existing OplockList to see whether we generate
    // 'STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE' (for read or read-write)

    while ((pOplockLink = PvfsListTraverse(pScb->pOplockList, pOplockLink)) != NULL)
    {
        pOplock = LW_STRUCT_FROM_FIELD(
                          pOplockLink,
                          PVFS_OPLOCK_RECORD,
                          OplockList);

        if (PvfsIrpContextCheckFlag(
                    pOplock->pIrpContext,
                    PVFS_IRP_CTX_FLAG_CANCELLED))
        {
            pOplock = NULL;
            continue;
        }

        if (pOplock->OplockType == IO_OPLOCK_REQUEST_LEASE &&
            (PvfsIsReadLease(pOplock->LeaseState) ||
             PvfsIsReadWriteLease(pOplock->LeaseState) ||
             PvfsIsReadHandleLease(pOplock->LeaseState) ||
             PvfsIsReadWriteHandleLease(pOplock->LeaseState)))
        {
            if (PvfsLeaseIsMine(pCcb, pOplock))
            {
                // Cannot ever have two different lease entries under
                // the same key
                LWIO_ASSERT(leaseToUpgrade == NULL);

                // Have to process everything before we know if we can
                // upgrade our lease
                leaseToUpgrade = pOplock;
            }
            else
            {
                ntError = STATUS_OPLOCK_NOT_GRANTED;
                BAIL_ON_NT_STATUS(ntError);
            }
        }
        else
        {
            // this should not happen as it has already been checked upfront
            LWIO_ASSERT(FALSE);
        }
    }

    if (leaseToUpgrade)
    {
        // remove pOplock
        PvfsListRemoveItem(pScb->pOplockList, &leaseToUpgrade->OplockList);

        // should be at most one present
        LWIO_ASSERT(PvfsListIsEmpty(pScb->pOplockList));

        leaseToUpgrade->pIrpContext->pIrp->IoStatusBlock.Status = STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE;
        PvfsAsyncCompleteIrpContext(leaseToUpgrade->pIrpContext);

        PvfsFreeOplockRecord(&leaseToUpgrade);
    }

    // Grant RWH lease

    ntError = PvfsAddLeaseRecord(
                  pScb,
                  pIrpContext,
                  pCcb,
                  IO_LEASE_STATE_READ|IO_LEASE_STATE_WRITE|IO_LEASE_STATE_HANDLE);
    BAIL_ON_NT_STATUS(ntError);

    ntError = STATUS_SUCCESS;

error:

    LWIO_UNLOCK_MUTEX(bScbControlLocked, &pScb->BaseControlBlock.Mutex);

    return ntError;
}

////////////////////////////////////////////////////////////////////////

static
BOOLEAN
PvfsIsSameLeaseKey(
    IN PPVFS_LEASE_KEY pLeaseKey1,
    IN PPVFS_LEASE_KEY pLeaseKey2
    )
{
    BOOLEAN equal = FALSE;

    // Two NULL lease keys are not considered the same

    if ((pLeaseKey1 != NULL) && (pLeaseKey2 != NULL))
    {
        if (PvfsLeaseKeyCompare(pLeaseKey1, pLeaseKey2) == 0)
        {
            equal = TRUE;
        }
    }

    return equal;
}

static
BOOLEAN
PvfsStreamAllOtherOpensHasSameOplockKey(
    IN PPVFS_SCB pScb
    )
{
    PLW_LIST_LINKS pCursor = NULL;
    PPVFS_CCB pCurrCcb = NULL;
    PPVFS_CCB pPrevCcb = NULL;
    BOOLEAN bLocked = FALSE;
    BOOLEAN bHasSameOplockKey = TRUE;

    LWIO_LOCK_RWMUTEX_SHARED(bLocked, &pScb->rwCcbLock);

    while ((pCursor = PvfsListTraverse(pScb->pCcbList, pCursor)) != NULL)
    {
        pCurrCcb = LW_STRUCT_FROM_FIELD(
                   pCursor,
                   PVFS_CCB,
                   ScbList);

        if (pPrevCcb && !PvfsIsSameLeaseKey(pCurrCcb->LeaseKey, pPrevCcb->LeaseKey))
        {
            bHasSameOplockKey = FALSE;
            break;
        }

        pPrevCcb = pCurrCcb;
        pCurrCcb = NULL;
    }

    LWIO_UNLOCK_RWMUTEX(bLocked, &pScb->rwCcbLock);

    return bHasSameOplockKey;
}

/*****************************************************************************
 ****************************************************************************/

static
BOOLEAN
PvfsLeaseIsMine(
    PPVFS_CCB pCcb,
    PPVFS_OPLOCK_RECORD pOplock
    )
{
    BOOLEAN isMine = FALSE;

    if ((pCcb == pOplock->pCcb) ||
        PvfsIsSameLeaseKey(pCcb->LeaseKey, pOplock->pCcb->LeaseKey))
    {
        isMine = TRUE;
    }

    return isMine;
}



NTSTATUS
PvfsLeaseBreakOnCreate(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;

    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:

            switch(pIrpContext->pIrp->Args.Create.CreateDisposition)
            {
                case FILE_SUPERSEDE:
                case FILE_OVERWRITE:
                case FILE_OVERWRITE_IF:
                    BreakResult = IO_OPLOCK_BROKEN_LEASE;
                    NewLeaseState = IO_LEASE_STATE_NONE;

                    LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

                    if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
                    {
                        ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                        LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                        BAIL_ON_NT_STATUS(ntError);
                    }

                    pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_NONE;
                    pOplock->pCcb->OplockBreakResult = BreakResult;

                    LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

                    ntError = STATUS_SUCCESS;

                    break;
                default:
                    BreakResult = IO_OPLOCK_NOT_BROKEN;
                    break;
            }

            ntError = STATUS_SUCCESS;
            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
            switch(pIrpContext->pIrp->Args.Create.CreateDisposition)
            {
                case FILE_SUPERSEDE:
                case FILE_OVERWRITE:
                case FILE_OVERWRITE_IF:
                    BreakResult = IO_OPLOCK_BROKEN_LEASE;
                    NewLeaseState = IO_LEASE_STATE_NONE;
                    break;

                default:
                    BreakResult = IO_OPLOCK_NOT_BROKEN;
                    break;
            }

            if (BreakResult == IO_OPLOCK_BROKEN_LEASE)
            {
                LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

                if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
                {
                    ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                    LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                    BAIL_ON_NT_STATUS(ntError);
                }

                pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
                pOplock->pCcb->OplockBreakResult = BreakResult;

                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

                pScb->bOplockBreakInProgress = TRUE;

                ntError = STATUS_PENDING;
            }
            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:

            switch(pIrpContext->pIrp->Args.Create.CreateDisposition)
            {
                case FILE_SUPERSEDE:
                case FILE_OVERWRITE:
                case FILE_OVERWRITE_IF:
                    BreakResult = IO_OPLOCK_BROKEN_LEASE;
                    NewLeaseState = IO_LEASE_STATE_NONE;
                    break;

                default:
                    BreakResult = IO_OPLOCK_BROKEN_LEASE;
                    NewLeaseState = IO_LEASE_STATE_READ;
                    break;
            }

            if (BreakResult == IO_OPLOCK_BROKEN_LEASE)
            {
                LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

                if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
                {
                    ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                    LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                    BAIL_ON_NT_STATUS(ntError);
                }

                pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
                pOplock->pCcb->OplockBreakResult = BreakResult;

                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

                pScb->bOplockBreakInProgress = TRUE;

                ntError = STATUS_PENDING;
            }
            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            switch(pIrpContext->pIrp->Args.Create.CreateDisposition)
            {
                case FILE_SUPERSEDE:
                case FILE_OVERWRITE:
                case FILE_OVERWRITE_IF:
                    BreakResult = IO_OPLOCK_BROKEN_LEASE;
                    NewLeaseState = IO_LEASE_STATE_NONE;
                    break;

                default:
                    BreakResult = IO_OPLOCK_BROKEN_LEASE;
                    NewLeaseState = IO_LEASE_STATE_READ|IO_LEASE_STATE_HANDLE;
                    break;
            }

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;

            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsLeaseBreakOnRead(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;


    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_NOT_BROKEN;
            ntError = STATUS_SUCCESS;

            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            if ((IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE) == pOplock->LeaseState)
            {
                NewLeaseState = IO_LEASE_STATE_READ;
            }
            else
            {
                NewLeaseState = IO_LEASE_STATE_READ|IO_LEASE_STATE_HANDLE;
            }

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;

            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsLeaseBreakOnWrite(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;


    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            NewLeaseState = IO_LEASE_STATE_NONE;

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_NONE;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            ntError = STATUS_SUCCESS;

            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            NewLeaseState = IO_LEASE_STATE_NONE;

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;
            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsLeaseBreakOnLockControl(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;


    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            NewLeaseState = IO_LEASE_STATE_NONE;

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_NONE;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            ntError = STATUS_SUCCESS;

            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            NewLeaseState = IO_LEASE_STATE_NONE;

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;
            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsLeaseBreakOnSetFileInformationEoFOrAllocation(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;


    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            NewLeaseState = IO_LEASE_STATE_NONE;

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_NONE;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            ntError = STATUS_SUCCESS;

            break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;
            NewLeaseState = IO_LEASE_STATE_NONE;

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;
            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsLeaseBreakOnSetFileInformationRenameOrLink(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;


    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:

            BreakResult = IO_OPLOCK_NOT_BROKEN;
            ntError = STATUS_SUCCESS;

        break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;

            if ((IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE) == pOplock->LeaseState)
            {
                NewLeaseState = IO_LEASE_STATE_READ;
            }
            else
            {
                NewLeaseState = IO_LEASE_STATE_READ|IO_LEASE_STATE_WRITE;
            }

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;
            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

NTSTATUS
PvfsLeaseBreakOnSetFileInformationDisposition(
    IN  PPVFS_IRP_CONTEXT pIrpContext,
    IN  PPVFS_SCB pScb,
    IN  PPVFS_CCB pCcb,
    IN  PPVFS_OPLOCK_RECORD pOplock,
    OUT PULONG pBreakResult,
    OUT PIO_LEASE_STATE pNewLeaseState
    )
{
    NTSTATUS ntError = STATUS_SUCCESS;
    BOOLEAN bCcbLocked = FALSE;
    ULONG BreakResult = IO_OPLOCK_NOT_BROKEN;
    IO_LEASE_STATE NewLeaseState = IO_LEASE_STATE_NONE;


    /* break only if the oplock key is different */

    if (PvfsLeaseIsMine(pCcb, pOplock))
    {
        goto cleanup;
    }

    switch (pOplock->LeaseState)
    {
        case IO_LEASE_STATE_READ:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE:

            BreakResult = IO_OPLOCK_NOT_BROKEN;
            ntError = STATUS_SUCCESS;

        break;

        case IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE:
        case IO_LEASE_STATE_READ | IO_LEASE_STATE_WRITE | IO_LEASE_STATE_HANDLE:

            BreakResult = IO_OPLOCK_BROKEN_LEASE;

            if ((IO_LEASE_STATE_READ | IO_LEASE_STATE_HANDLE) == pOplock->LeaseState)
            {
                NewLeaseState = IO_LEASE_STATE_READ;
            }
            else
            {
                NewLeaseState = IO_LEASE_STATE_READ|IO_LEASE_STATE_WRITE;
            }

            LWIO_LOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            if (pOplock->pCcb->OplockState != PVFS_OPLOCK_STATE_GRANTED)
            {
                ntError = STATUS_INVALID_OPLOCK_PROTOCOL;
                LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);
                BAIL_ON_NT_STATUS(ntError);
            }

            pOplock->pCcb->OplockState = PVFS_OPLOCK_STATE_BREAK_IN_PROGRESS;
            pOplock->pCcb->OplockBreakResult = BreakResult;

            LWIO_UNLOCK_MUTEX(bCcbLocked, &pOplock->pCcb->ControlBlock);

            pScb->bOplockBreakInProgress = TRUE;

            ntError = STATUS_PENDING;
            break;

        default:
            ntError = STATUS_INVALID_PARAMETER;
    }
    BAIL_ON_NT_STATUS(ntError);

cleanup:
    *pBreakResult = BreakResult;
    *pNewLeaseState = NewLeaseState;

    return ntError;

error:
    goto cleanup;
}

BOOLEAN
PvfsLeaseBreakAckRequired(
    IN IO_LEASE_STATE CurrentLeaseState,
    IN IO_LEASE_STATE NewLeaseState
    )
{
    BOOLEAN ackRequired = FALSE;

    if (IsSetFlag(CurrentLeaseState, IO_LEASE_STATE_WRITE) &&
        !IsSetFlag(NewLeaseState, IO_LEASE_STATE_WRITE))
    {
        ackRequired = TRUE;
    }
    else if (IsSetFlag(CurrentLeaseState, IO_LEASE_STATE_HANDLE) &&
             !IsSetFlag(NewLeaseState, IO_LEASE_STATE_HANDLE))
    {
        ackRequired = TRUE;
    }

    return ackRequired;
}
