/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    LockCtrl.c

Abstract:

    This module implements the Lock Control routines for Udfs called
    by the Fsd/Fsp dispatch driver.

// @@BEGIN_DDKSPLIT

Author:

    Dan Lovinger    [DanLo]     20-Jan-1997

Revision History:

// @@END_DDKSPLIT

--*/

#include "UdfProcs.h"

//
//  The Bug check file id for this module
//

#define BugCheckFileId                   (UDFS_BUG_CHECK_LOCKCTRL)

//
//  The local debug trace level
//

#define Dbg                              (UDFS_DEBUG_LEVEL_LOCKCTRL)

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, UdfCommonLockControl)
#pragma alloc_text(PAGE, UdfFastLock)
#pragma alloc_text(PAGE, UdfFastUnlockAll)
#pragma alloc_text(PAGE, UdfFastUnlockAllByKey)
#pragma alloc_text(PAGE, UdfFastUnlockSingle)
#endif


NTSTATUS
UdfCommonLockControl (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for Lock Control called by both the fsd and fsp
    threads.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );

    TYPE_OF_OPEN TypeOfOpen;
    PFCB Fcb;
    PCCB Ccb;

    PAGED_CODE();

    //
    //  Extract and decode the type of file object we're being asked to process
    //

    TypeOfOpen = UdfDecodeFileObject( IrpSp->FileObject, &Fcb, &Ccb );

    //
    //  If the file is not a user file open then we reject the request
    //  as an invalid parameter
    //

    if (TypeOfOpen != UserFileOpen) {

        UdfCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
        return STATUS_INVALID_PARAMETER;
    }

    //
    //  We check whether we can proceed based on the state of the file oplocks.
    //  This call might post the irp for us.
    //

    Status = FsRtlCheckOplock( &Fcb->Oplock,
                               Irp,
                               IrpContext,
                               UdfOplockComplete,
                               NULL );

    //
    //  If we don't get success then the oplock package completed the request.
    //

    if (Status != STATUS_SUCCESS) {

        return Status;
    }

    //
    //  Verify the Fcb.
    //

    UdfVerifyFcbOperation( IrpContext, Fcb );

    //
    //  If we don't have a file lock, then get one now.
    //

    if (Fcb->FileLock == NULL) { UdfCreateFileLock( IrpContext, Fcb, TRUE ); }

    //
    //  Now call the FsRtl routine to do the actual processing of the
    //  Lock request
    //

    Status = FsRtlProcessFileLock( Fcb->FileLock, Irp, NULL );

    //
    //  Set the flag indicating if Fast I/O is possible
    //

    UdfLockFcb( IrpContext, Fcb );
    Fcb->IsFastIoPossible = UdfIsFastIoPossible( Fcb );
    UdfUnlockFcb( IrpContext, Fcb );

    //
    //  Complete the request.
    //

    UdfCompleteRequest( IrpContext, NULL, Status );
    return Status;
}


BOOLEAN
UdfFastLock (
    IN PFILE_OBJECT FileObject,
    IN PLARGE_INTEGER FileOffset,
    IN PLARGE_INTEGER Length,
    PEPROCESS ProcessId,
    ULONG Key,
    BOOLEAN FailImmediately,
    BOOLEAN ExclusiveLock,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This is a call back routine for doing the fast lock call.

Arguments:

    FileObject - Supplies the file object used in this operation

    FileOffset - Supplies the file offset used in this operation

    Length - Supplies the length used in this operation

    ProcessId - Supplies the process ID used in this operation

    Key - Supplies the key used in this operation

    FailImmediately - Indicates if the request should fail immediately
        if the lock cannot be granted.

    ExclusiveLock - Indicates if this is a request for an exclusive or
        shared lock

    IoStatus - Receives the Status if this operation is successful

Return Value:

    BOOLEAN - TRUE if this operation completed and FALSE if caller
        needs to take the long route.

--*/

{
    BOOLEAN Results = FALSE;

    PFCB Fcb;
    TYPE_OF_OPEN TypeOfOpen;

    PAGED_CODE();

    ASSERT_FILE_OBJECT( FileObject );

    IoStatus->Information = 0;

    //
    //  Decode the type of file object we're being asked to process and
    //  make sure that is is only a user file open.
    //

    TypeOfOpen = UdfFastDecodeFileObject( FileObject, &Fcb );

    if (TypeOfOpen != UserFileOpen) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //
    //  Only deal with 'good' Fcb's.
    //

    if (!UdfVerifyFcbOperation( NULL, Fcb )) {

        return FALSE;
    }

    FsRtlEnterFileSystem();

    //
    //  Use a try-finally to facilitate cleanup.
    //

    try {

        //
        //  We check whether we can proceed based on the state of the file oplocks.
        //

        if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) {

            try_leave( NOTHING );
        }

        //
        //  If we don't have a file lock, then get one now.
        //

        if ((Fcb->FileLock == NULL) && !UdfCreateFileLock( NULL, Fcb, FALSE )) {

            try_leave( NOTHING );
        }

        //
        //  Now call the FsRtl routine to perform the lock request.
        //

        if (Results = FsRtlFastLock( Fcb->FileLock,
                                     FileObject,
                                     FileOffset,
                                     Length,
                                     ProcessId,
                                     Key,
                                     FailImmediately,
                                     ExclusiveLock,
                                     IoStatus,
                                     NULL,
                                     FALSE )) {

            //
            //  Set the flag indicating if Fast I/O is questionable.  We
            //  only change this flag if the current state is possible.
            //  Retest again after synchronizing on the header.
            //

            if (Fcb->IsFastIoPossible == FastIoIsPossible) {

                UdfLockFcb( NULL, Fcb );
                Fcb->IsFastIoPossible = UdfIsFastIoPossible( Fcb );
                UdfUnlockFcb( NULL, Fcb );
            }
        }

    } finally {

        FsRtlExitFileSystem();
    }

    return Results;
}


BOOLEAN
UdfFastUnlockSingle (
    IN PFILE_OBJECT FileObject,
    IN PLARGE_INTEGER FileOffset,
    IN PLARGE_INTEGER Length,
    PEPROCESS ProcessId,
    ULONG Key,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This is a call back routine for doing the fast unlock single call.

Arguments:

    FileObject - Supplies the file object used in this operation

    FileOffset - Supplies the file offset used in this operation

    Length - Supplies the length used in this operation

    ProcessId - Supplies the process ID used in this operation

    Key - Supplies the key used in this operation

    Status - Receives the Status if this operation is successful

Return Value:

    BOOLEAN - TRUE if this operation completed and FALSE if caller
        needs to take the long route.

--*/

{
    BOOLEAN Results = FALSE;
    TYPE_OF_OPEN TypeOfOpen;
    PFCB Fcb;

    PAGED_CODE();

    IoStatus->Information = 0;

    //
    //  Decode the type of file object we're being asked to process and
    //  make sure that is is only a user file open.
    //

    TypeOfOpen = UdfFastDecodeFileObject( FileObject, &Fcb );

    if (TypeOfOpen != UserFileOpen) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //
    //  Only deal with 'good' Fcb's.
    //

    if (!UdfVerifyFcbOperation( NULL, Fcb )) {

        return FALSE;
    }

    //
    //  If there is no lock then return immediately.
    //

    if (Fcb->FileLock == NULL) {

        IoStatus->Status = STATUS_RANGE_NOT_LOCKED;
        return TRUE;
    }

    FsRtlEnterFileSystem();

    try {

        //
        //  We check whether we can proceed based on the state of the file oplocks.
        //

        if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) {

            try_leave( NOTHING );
        }

        //
        //  If we don't have a file lock, then get one now.
        //

        if ((Fcb->FileLock == NULL) && !UdfCreateFileLock( NULL, Fcb, FALSE )) {

            try_leave( NOTHING );
        }

        //
        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request.  The call will always succeed.
        //

        Results = TRUE;
        IoStatus->Status = FsRtlFastUnlockSingle( Fcb->FileLock,
                                                  FileObject,
                                                  FileOffset,
                                                  Length,
                                                  ProcessId,
                                                  Key,
                                                  NULL,
                                                  FALSE );

        //
        //  Set the flag indicating if Fast I/O is possible.  We are
        //  only concerned if there are no longer any filelocks on this
        //  file.
        //

        if (!FsRtlAreThereCurrentFileLocks( Fcb->FileLock ) &&
            (Fcb->IsFastIoPossible != FastIoIsPossible)) {

            UdfLockFcb( IrpContext, Fcb );
            Fcb->IsFastIoPossible = UdfIsFastIoPossible( Fcb );
            UdfUnlockFcb( IrpContext, Fcb );
        }

    } finally {

        FsRtlExitFileSystem();
    }

    return Results;
}


BOOLEAN
UdfFastUnlockAll (
    IN PFILE_OBJECT FileObject,
    PEPROCESS ProcessId,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This is a call back routine for doing the fast unlock all call.

Arguments:

    FileObject - Supplies the file object used in this operation

    ProcessId - Supplies the process ID used in this operation

    Status - Receives the Status if this operation is successful

Return Value:

    BOOLEAN - TRUE if this operation completed and FALSE if caller
        needs to take the long route.

--*/

{
    BOOLEAN Results = FALSE;
    TYPE_OF_OPEN TypeOfOpen;
    PFCB Fcb;

    PAGED_CODE();

    IoStatus->Information = 0;

    //
    //  Decode the type of file object we're being asked to process and
    //  make sure that is is only a user file open.
    //

    TypeOfOpen = UdfFastDecodeFileObject( FileObject, &Fcb );

    if (TypeOfOpen != UserFileOpen) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //
    //  Only deal with 'good' Fcb's.
    //

    if (!UdfVerifyFcbOperation( NULL, Fcb )) {

        return FALSE;
    }

    //
    //  If there is no lock then return immediately.
    //

    if (Fcb->FileLock == NULL) {

        IoStatus->Status = STATUS_RANGE_NOT_LOCKED;
        return TRUE;
    }

    FsRtlEnterFileSystem();

    try {

        //
        //  We check whether we can proceed based on the state of the file oplocks.
        //

        if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) {

            try_leave( NOTHING );
        }

        //
        //  If we don't have a file lock, then get one now.
        //

        if ((Fcb->FileLock == NULL) && !UdfCreateFileLock( NULL, Fcb, FALSE )) {

            try_leave( NOTHING );
        }

        //
        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request.  The call will always succeed.
        //

        Results = TRUE;
        IoStatus->Status = FsRtlFastUnlockAll( Fcb->FileLock,
                                               FileObject,
                                               ProcessId,
                                               NULL );


        //
        //  Set the flag indicating if Fast I/O is possible
        //

        UdfLockFcb( IrpContext, Fcb );
        Fcb->IsFastIoPossible = UdfIsFastIoPossible( Fcb );
        UdfUnlockFcb( IrpContext, Fcb );

    } finally {

        FsRtlExitFileSystem();
    }

    return Results;
}


BOOLEAN
UdfFastUnlockAllByKey (
    IN PFILE_OBJECT FileObject,
    PVOID ProcessId,
    ULONG Key,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

Routine Description:

    This is a call back routine for doing the fast unlock all by key call.

Arguments:

    FileObject - Supplies the file object used in this operation

    ProcessId - Supplies the process ID used in this operation

    Key - Supplies the key used in this operation

    Status - Receives the Status if this operation is successful

Return Value:

    BOOLEAN - TRUE if this operation completed and FALSE if caller
        needs to take the long route.

--*/

{
    BOOLEAN Results = FALSE;
    TYPE_OF_OPEN TypeOfOpen;
    PFCB Fcb;

    PAGED_CODE();

    IoStatus->Information = 0;

    //
    //  Decode the type of file object we're being asked to process and
    //  make sure that is is only a user file open.
    //

    TypeOfOpen = UdfFastDecodeFileObject( FileObject, &Fcb );

    if (TypeOfOpen != UserFileOpen) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //
    //  Only deal with 'good' Fcb's.
    //

    if (!UdfVerifyFcbOperation( NULL, Fcb )) {

        return FALSE;
    }

    //
    //  If there is no lock then return immediately.
    //

    if (Fcb->FileLock == NULL) {

        IoStatus->Status = STATUS_RANGE_NOT_LOCKED;
        return TRUE;
    }

    FsRtlEnterFileSystem();

    try {

        //
        //  We check whether we can proceed based on the state of the file oplocks.
        //

        if ((Fcb->Oplock != NULL) && !FsRtlOplockIsFastIoPossible( &Fcb->Oplock )) {

            try_leave( NOTHING );
        }

        //
        //  If we don't have a file lock, then get one now.
        //

        if ((Fcb->FileLock == NULL) && !UdfCreateFileLock( NULL, Fcb, FALSE )) {

            try_leave( NOTHING );
        }

        //
        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request.  The call will always succeed.
        //

        Results = TRUE;
        IoStatus->Status = FsRtlFastUnlockAllByKey( Fcb->FileLock,
                                                    FileObject,
                                                    ProcessId,
                                                    Key,
                                                    NULL );


        //
        //  Set the flag indicating if Fast I/O is possible
        //

        UdfLockFcb( IrpContext, Fcb );
        Fcb->IsFastIoPossible = UdfIsFastIoPossible( Fcb );
        UdfUnlockFcb( IrpContext, Fcb );

    } finally {

        FsRtlExitFileSystem();
    }

    return Results;
}


