////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////
/*************************************************************************
*
* File: LockCtrl.cpp.cpp
*
* Module: UDF File System Driver (Kernel mode execution only)
*
* Description:
*   Contains code to handle the "byte-range locking" dispatch entry point.
*
*************************************************************************/

#include            "udffs.h"

// define the file specific bug-check id
#define         UDF_BUG_CHECK_ID                UDF_FILE_SHUTDOWN


/*************************************************************************
*
* Function: UDFLockControl()
*
* Description:
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: Irrelevant.
*
*************************************************************************/
NTSTATUS
NTAPI
UDFLockControl(
    IN PDEVICE_OBJECT DeviceObject,       // the logical volume device object
    IN PIRP           Irp)                // I/O Request Packet
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PtrUDFIrpContext    PtrIrpContext = NULL;
    BOOLEAN             AreWeTopLevel = FALSE;

    UDFPrint(("UDFLockControl\n"));
//    BrutePoint();

    FsRtlEnterFileSystem();
    ASSERT(DeviceObject);
    ASSERT(Irp);

    // set the top level context
    AreWeTopLevel = UDFIsIrpTopLevel(Irp);
    //  Call the common Lock Control routine, with blocking allowed if
    //  synchronous
    _SEH2_TRY {

        // get an IRP context structure and issue the request
        PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
        if(PtrIrpContext) {
            RC = UDFCommonLockControl(PtrIrpContext, Irp);
        } else {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Status = RC;
            Irp->IoStatus.Information = 0;
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

    } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {

        RC = UDFExceptionHandler(PtrIrpContext, Irp);

        UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
    } _SEH2_END;

    if (AreWeTopLevel) {
        IoSetTopLevelIrp(NULL);
    }

    FsRtlExitFileSystem();

    return(RC);
} // end UDFLockControl()


/*************************************************************************
*
* Function: UDFCommonLockControl()
*
* Description:
*  This is the common routine for doing Lock control operations called
*  by both the fsd and fsp threads
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: Irrelevant
*
*************************************************************************/
NTSTATUS
NTAPI
UDFCommonLockControl(
    IN PtrUDFIrpContext PtrIrpContext,
    IN PIRP             Irp)
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PIO_STACK_LOCATION  IrpSp = NULL;
    //IO_STATUS_BLOCK     LocalIoStatus;
//    BOOLEAN             CompleteRequest = FALSE;
    BOOLEAN             PostRequest = FALSE;
    BOOLEAN             CanWait = FALSE;
    PtrUDFNTRequiredFCB NtReqFcb = NULL;
    BOOLEAN             AcquiredFCB = FALSE;
    PFILE_OBJECT        FileObject = NULL;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;

    UDFPrint(("UDFCommonLockControl\n"));

    _SEH2_TRY {
        // First, get a pointer to the current I/O stack location.
        IrpSp = IoGetCurrentIrpStackLocation(Irp);
        ASSERT(IrpSp);

        FileObject = IrpSp->FileObject;
        ASSERT(FileObject);

        // Get the FCB and CCB pointers.
        Ccb = (PtrUDFCCB)(FileObject->FsContext2);
        ASSERT(Ccb);
        Fcb = Ccb->Fcb;
        ASSERT(Fcb);
        // Validate the sent-in FCB
        if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
             (Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {

//            CompleteRequest = TRUE;
            try_return(RC = STATUS_INVALID_PARAMETER);
        }

        NtReqFcb = Fcb->NTRequiredFCB;
        CanWait = ((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);

        // Acquire the FCB resource shared
        UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
        if (!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
            PostRequest = TRUE;
            try_return(RC = STATUS_PENDING);
        }
        AcquiredFCB = TRUE;

        RC = FsRtlProcessFileLock(&(NtReqFcb->FileLock), Irp, NULL);
//        CompleteRequest = TRUE;

try_exit: NOTHING;

    } _SEH2_FINALLY {

        // Release the FCB resources if acquired.
        if (AcquiredFCB) {
            UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
            UDFReleaseResource(&(NtReqFcb->MainResource));
            AcquiredFCB = FALSE;
        }
        if (PostRequest) {
            // Perform appropriate post related processing here
            RC = UDFPostRequest(PtrIrpContext, Irp);
        } else
        if(!_SEH2_AbnormalTermination()) {
            // Simply free up the IrpContext since the IRP has been queued or
            // Completed by FsRtlProcessFileLock
            UDFReleaseIrpContext(PtrIrpContext);
        }
    } _SEH2_END; // end of "__finally" processing

    return(RC);
} // end UDFCommonLockControl()


/*
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
NTAPI
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
    )
{
    BOOLEAN Results = FALSE;

//    BOOLEAN             AcquiredFCB = FALSE;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;

    UDFPrint(("UDFFastLock\n"));
    //  Decode the type of file object we're being asked to process and make
    //  sure it is only a user file open.


    // Get the FCB and CCB pointers.
    Ccb = (PtrUDFCCB)(FileObject->FsContext2);
    ASSERT(Ccb);
    Fcb = Ccb->Fcb;
    ASSERT(Fcb);
    // Validate the sent-in FCB
    if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
         (Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        IoStatus->Information = 0;
        return TRUE;
    }

    //  Acquire exclusive access to the Fcb this operation can always wait

    FsRtlEnterFileSystem();

    // BUGBUG: kenr
    // (VOID) ExAcquireResourceShared( Fcb->Header.Resource, TRUE );

    _SEH2_TRY {

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

        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request
        if ((Results = FsRtlFastLock( &(Fcb->NTRequiredFCB->FileLock),
                                     FileObject,
                                     FileOffset,
                                     Length,
                                     ProcessId,
                                     Key,
                                     FailImmediately,
                                     ExclusiveLock,
                                     IoStatus,
                                     NULL,
                                     FALSE ))) {

            //  Set the flag indicating if Fast I/O is possible
            Fcb->NTRequiredFCB->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
        }

//try_exit:  NOTHING;
    } _SEH2_FINALLY {

        //  Release the Fcb, and return to our caller

        // BUGBUG: kenr
        //    UDFReleaseResource( (Fcb)->Header.Resource );

        FsRtlExitFileSystem();

    } _SEH2_END;

    return Results;
} // end UDFFastLock()


/*
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
NTAPI
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
    )

{
    BOOLEAN Results = FALSE;

//    BOOLEAN             AcquiredFCB = FALSE;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;

    UDFPrint(("UDFFastUnlockSingle\n"));
    //  Decode the type of file object we're being asked to process and make
    //  sure it is only a user file open.

    IoStatus->Information = 0;

    // Get the FCB and CCB pointers.
    Ccb = (PtrUDFCCB)(FileObject->FsContext2);
    ASSERT(Ccb);
    Fcb = Ccb->Fcb;
    ASSERT(Fcb);
    // Validate the sent-in FCB
    if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
         (Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //  Acquire exclusive access to the Fcb this operation can always wait

    FsRtlEnterFileSystem();

    // BUGBUG: kenr
    // (VOID) ExAcquireResourceShared( Fcb->Header.Resource, TRUE );

    _SEH2_TRY {

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

        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request
        Results = TRUE;
        IoStatus->Status = FsRtlFastUnlockSingle( &(Fcb->NTRequiredFCB->FileLock),
                                                  FileObject,
                                                  FileOffset,
                                                  Length,
                                                  ProcessId,
                                                  Key,
                                                  NULL,
                                                  FALSE );
        //  Set the flag indicating if Fast I/O is possible
        Fcb->NTRequiredFCB->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);

//try_exit:  NOTHING;
    } _SEH2_FINALLY {

        //  Release the Fcb, and return to our caller

        // BUGBUG: kenr
        //    UDFReleaseResource( (Fcb)->Header.Resource );

        FsRtlExitFileSystem();

    } _SEH2_END;

    return Results;
} // end UDFFastUnlockSingle()


/*
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
NTAPI
UDFFastUnlockAll(
    IN PFILE_OBJECT FileObject,
    PEPROCESS ProcessId,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )

{
    BOOLEAN Results = FALSE;

//    BOOLEAN             AcquiredFCB = FALSE;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;

    UDFPrint(("UDFFastUnlockAll\n"));

    IoStatus->Information = 0;
    //  Decode the type of file object we're being asked to process and make
    //  sure it is only a user file open.

    // Get the FCB and CCB pointers.
    Ccb = (PtrUDFCCB)(FileObject->FsContext2);
    ASSERT(Ccb);
    Fcb = Ccb->Fcb;
    ASSERT(Fcb);
    // Validate the sent-in FCB
    if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
         (Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //  Acquire shared access to the Fcb this operation can always wait

    FsRtlEnterFileSystem();

    UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB);
    UDFAcquireResourceShared( &(Fcb->NTRequiredFCB->MainResource),TRUE );

    _SEH2_TRY {

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

        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request
        Results = TRUE;
        IoStatus->Status = FsRtlFastUnlockAll( &(Fcb->NTRequiredFCB->FileLock),
                                               FileObject,
                                               ProcessId,
                                               NULL );

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

        Fcb->NTRequiredFCB->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible( Fcb );

//try_exit:  NOTHING;
    } _SEH2_FINALLY {

        //  Release the Fcb, and return to our caller

        UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB);
        UDFReleaseResource(&(Fcb->NTRequiredFCB->MainResource));
        FsRtlExitFileSystem();

    } _SEH2_END;

    return Results;
} // end UDFFastUnlockAll()


/*
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
NTAPI
UDFFastUnlockAllByKey(
    IN PFILE_OBJECT FileObject,
    PEPROCESS ProcessId,
    ULONG Key,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )

{
    BOOLEAN Results = FALSE;

//    BOOLEAN             AcquiredFCB = FALSE;
    PtrUDFFCB           Fcb = NULL;
    PtrUDFCCB           Ccb = NULL;

    UDFPrint(("UDFFastUnlockAllByKey\n"));

    IoStatus->Information = 0;
    //  Decode the type of file object we're being asked to process and make
    //  sure it is only a user file open.

    // Get the FCB and CCB pointers.
    Ccb = (PtrUDFCCB)(FileObject->FsContext2);
    ASSERT(Ccb);
    Fcb = Ccb->Fcb;
    ASSERT(Fcb);
    // Validate the sent-in FCB
    if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
         (Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {

        IoStatus->Status = STATUS_INVALID_PARAMETER;
        return TRUE;
    }

    //  Acquire shared access to the Fcb this operation can always wait

    FsRtlEnterFileSystem();

    UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB);
    UDFAcquireResourceShared( &(Fcb->NTRequiredFCB->MainResource),TRUE );

    _SEH2_TRY {

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

        //  Now call the FsRtl routine to do the actual processing of the
        //  Lock request
        Results = TRUE;
        IoStatus->Status = FsRtlFastUnlockAllByKey( &(Fcb->NTRequiredFCB->FileLock),
                                                    FileObject,
                                                    ProcessId,
                                                    Key,
                                                    NULL );

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

        Fcb->NTRequiredFCB->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible( Fcb );

//try_exit:  NOTHING;
    } _SEH2_FINALLY {

        //  Release the Fcb, and return to our caller

        UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB);
        UDFReleaseResource(&(Fcb->NTRequiredFCB->MainResource));
        FsRtlExitFileSystem();

    } _SEH2_END;

    return Results;
} // end UDFFastUnlockAllByKey()