/*
 * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
 * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice,
 *   this list of conditions and the following disclaimer in the
 *   documentation
 *   and/or other materials provided with the distribution.
 * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
 *   nor the names of their contributors may be used to endorse or promote
 *   products derived from this software without specific prior written
 *   permission from Kernel Drivers, LLC and Your File System, Inc.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//
// File: AFSLockControl.cpp
//

#include "AFSCommon.h"

NTSTATUS
AFSLockControl( IN PDEVICE_OBJECT LibDeviceObject,
                  IN PIRP Irp)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    ULONG ulRequestType = 0;
    IO_STACK_LOCATION *pIrpSp = IoGetCurrentIrpStackLocation( Irp);
    AFSFcb *pFcb = NULL;
    AFSCcb *pCcb = NULL;
    BOOLEAN bCompleteRequest = TRUE;
    AFSByteRangeLockRequestCB  stLockRequestCB;
    AFSByteRangeLockResultCB  stLockResultCB;
    AFSByteRangeUnlockRequestCB stUnlockRequestCB;
    AFSByteRangeUnlockResultCB stUnlockResultCB;
    ULONG           ulResultLen = 0;
    BOOLEAN         bReleaseResource = FALSE;
    IO_STATUS_BLOCK stIoStatus;

    __try
    {

        pFcb = (AFSFcb *)pIrpSp->FileObject->FsContext;

        pCcb = (AFSCcb *)pIrpSp->FileObject->FsContext2;

        if( pFcb == NULL)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSLockControl Attempted access (%08lX) when pFcb == NULL\n",
                          Irp);

            try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
        }

        //
        // Acquire the main shared for adding the lock control to the list
        //

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "AFSLockControl Acquiring Fcb lock %08lX SHARED %08lX\n",
                      &pFcb->NPFcb->Resource,
                      PsGetCurrentThread());

        AFSAcquireShared( &pFcb->NPFcb->Resource,
                          TRUE);

        bReleaseResource = TRUE;

        if( pFcb->Header.NodeTypeCode == AFS_IOCTL_FCB)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSLockControl Failing request against PIOCtl Fcb\n");

            try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
        }
        else if( pFcb->Header.NodeTypeCode == AFS_SPECIAL_SHARE_FCB)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSLockControl Failing request against SpecialShare Fcb\n");

            try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
        }

        //
        // Post the request to the service for checks
        //

        switch( pIrpSp->MinorFunction)
        {

            case IRP_MN_LOCK:
            {

                stLockRequestCB.Count = 1;

                stLockRequestCB.ProcessId = (ULONGLONG)PsGetCurrentProcessId();

                stLockRequestCB.Request[ 0].LockType = AFS_BYTE_RANGE_LOCK_TYPE_EXCL;

                stLockRequestCB.Request[ 0].Offset = pIrpSp->Parameters.LockControl.ByteOffset;

                stLockRequestCB.Request[ 0].Length.QuadPart = pIrpSp->Parameters.LockControl.Length->QuadPart;

                ulResultLen = sizeof( AFSByteRangeLockResultCB);

                ntStatus = AFSProcessRequest( AFS_REQUEST_TYPE_BYTE_RANGE_LOCK,
                                              AFS_REQUEST_FLAG_SYNCHRONOUS,
                                              &pFcb->AuthGroup,
                                              &pCcb->DirectoryCB->NameInformation.FileName,
                                              &pFcb->ObjectInformation->FileId,
                                              &stLockRequestCB,
                                              sizeof( AFSByteRangeLockRequestCB),
                                              &stLockResultCB,
                                              &ulResultLen);

                if( !NT_SUCCESS( ntStatus))
                {

                    try_return( ntStatus);
                }

                break;
            }

            case IRP_MN_UNLOCK_ALL:
            case IRP_MN_UNLOCK_ALL_BY_KEY:
            {

                RtlZeroMemory( &stUnlockRequestCB,
                               sizeof( AFSByteRangeUnlockRequestCB));

                stUnlockRequestCB.ProcessId = (ULONGLONG)PsGetCurrentProcessId();

                ntStatus = AFSProcessRequest( AFS_REQUEST_TYPE_BYTE_RANGE_UNLOCK_ALL,
                                              AFS_REQUEST_FLAG_SYNCHRONOUS,
                                              &pFcb->AuthGroup,
                                              &pCcb->DirectoryCB->NameInformation.FileName,
                                              &pFcb->ObjectInformation->FileId,
                                              (void *)&stUnlockRequestCB,
                                              sizeof( AFSByteRangeUnlockRequestCB),
                                              NULL,
                                              NULL);

                if( NT_SUCCESS( ntStatus))
                {
                    CcFlushCache( &pFcb->NPFcb->SectionObjectPointers,
                                  NULL,
                                  0,
                                  &stIoStatus);

                    if( !NT_SUCCESS( stIoStatus.Status))
                    {

                        AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
                                      AFS_TRACE_LEVEL_ERROR,
                                      "AFSLockControl CcFlushCache [1] failure FID %08lX-%08lX-%08lX-%08lX Status 0x%08lX Bytes 0x%08lX\n",
                                      pFcb->ObjectInformation->FileId.Cell,
                                      pFcb->ObjectInformation->FileId.Volume,
                                      pFcb->ObjectInformation->FileId.Vnode,
                                      pFcb->ObjectInformation->FileId.Unique,
                                      stIoStatus.Status,
                                      stIoStatus.Information);

                        ntStatus = stIoStatus.Status;
                    }
                }

                //
                // Even on a failure we need to notify the rtl package of the unlock
                //

                break;
            }

            case IRP_MN_UNLOCK_SINGLE:
            {

                stUnlockRequestCB.Count = 1;

                stUnlockRequestCB.ProcessId = (ULONGLONG)PsGetCurrentProcessId();

                stUnlockRequestCB.Request[ 0].LockType = AFS_BYTE_RANGE_LOCK_TYPE_EXCL;

                stUnlockRequestCB.Request[ 0].Offset = pIrpSp->Parameters.LockControl.ByteOffset;

                stUnlockRequestCB.Request[ 0].Length.QuadPart = pIrpSp->Parameters.LockControl.Length->QuadPart;

                ulResultLen = sizeof( AFSByteRangeUnlockResultCB);

                ntStatus = AFSProcessRequest( AFS_REQUEST_TYPE_BYTE_RANGE_UNLOCK,
                                              AFS_REQUEST_FLAG_SYNCHRONOUS,
                                              &pFcb->AuthGroup,
                                              &pCcb->DirectoryCB->NameInformation.FileName,
                                              &pFcb->ObjectInformation->FileId,
                                              (void *)&stUnlockRequestCB,
                                              sizeof( AFSByteRangeUnlockRequestCB),
                                              (void *)&stUnlockResultCB,
                                              &ulResultLen);

                if( NT_SUCCESS( ntStatus))
                {
                    CcFlushCache( &pFcb->NPFcb->SectionObjectPointers,
                                  &pIrpSp->Parameters.LockControl.ByteOffset,
                                  pIrpSp->Parameters.LockControl.Length->LowPart,
                                  &stIoStatus);

                    if( !NT_SUCCESS( stIoStatus.Status))
                    {

                        AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
                                      AFS_TRACE_LEVEL_ERROR,
                                      "AFSLockControl CcFlushCache [2] failure FID %08lX-%08lX-%08lX-%08lX Status 0x%08lX Bytes 0x%08lX\n",
                                      pFcb->ObjectInformation->FileId.Cell,
                                      pFcb->ObjectInformation->FileId.Volume,
                                      pFcb->ObjectInformation->FileId.Vnode,
                                      pFcb->ObjectInformation->FileId.Unique,
                                      stIoStatus.Status,
                                      stIoStatus.Information);

                        ntStatus = stIoStatus.Status;
                    }
                }

                break;
            }

            default:

                break;
        }

        //
        // Below here we won't complete the request, it is handled by the lock package
        //

        bCompleteRequest = FALSE;

        //
        //  Now call the system package for actually processing the lock request
        //

        ntStatus = FsRtlProcessFileLock( &pFcb->Specific.File.FileLock,
                                         Irp,
                                         NULL);

try_exit:

        if( bReleaseResource)
        {

            //
            // And drop it
            //

            AFSReleaseResource( &pFcb->NPFcb->Resource);
        }

        if( bCompleteRequest)
        {

            AFSCompleteRequest( Irp,
                                ntStatus);
        }
    }
    __except( AFSExceptionFilter( GetExceptionCode(), GetExceptionInformation()))
    {

        AFSDbgLogMsg( 0,
                      0,
                      "EXCEPTION - AFSLockControl\n");

        //
        // Again, there is little point in failing this request but pass back some type of failure status
        //

        ntStatus = STATUS_UNSUCCESSFUL;

        AFSCompleteRequest( Irp,
                            ntStatus);
    }

    return ntStatus;
}
