///////////////////////////////////////////////////////////////////////////////
//
//    (C) Copyright 1995 - 2005 OSR Open Systems Resources, Inc.
//    All Rights Reserved
//
//    This sofware is supplied for instructional purposes only.
//
//    OSR Open Systems Resources, Inc. (OSR) expressly disclaims any warranty
//    for this software.  THIS SOFTWARE IS PROVIDED  "AS IS" WITHOUT WARRANTY
//    OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
//    THE IMPLIED WARRANTIES OF MECHANTABILITY OR FITNESS FOR A PARTICULAR
//    PURPOSE.  THE ENTIRE RISK ARISING FROM THE USE OF THIS SOFTWARE REMAINS
//    WITH YOU.  OSR's entire liability and your exclusive remedy shall not
//    exceed the price paid for this material.  In no event shall OSR or its
//    suppliers be liable for any damages whatsoever (including, without
//    limitation, damages for loss of business profit, business interruption,
//    loss of business information, or any other pecuniary loss) arising out
//    of the use or inability to use this software, even if OSR has been
//    advised of the possibility of such damages.  Because some states/
//    jurisdictions do not allow the exclusion or limitation of liability for
//    consequential or incidental damages, the above limitation may not apply
//    to you.
//
//    OSR Open Systems Resources, Inc.
//    105 Route 101A Suite 19
//    Amherst, NH 03031  (603) 595-6500 FAX: (603) 595-6503
//    email bugs to: bugs@osr.com
//
//
//    MODULE:
//
//        RedirectIO.C -- Redirect driver V2 - Read/Write/Ioctl functions
//
//    ABSTRACT:
//
//      This file contains the skeleton status for a WDM/Win2K device driver
//      that does absolutely Redirect.
//
//    AUTHOR(S):
//
//        OSR Open Systems Resources, Inc.
// 
//    REVISION:   
//
//        This is a WDM compatible version of the original NT V4
//        REDIRECT driver.
//
///////////////////////////////////////////////////////////////////////////////

#include "redirect.h"

BOOLEAN DoRedirect = TRUE;

///////////////////////////////////////////////////////////////////////////////
//
//  RedirectCreate
//
//    This is the dispatch entry point for processing IRP_MJ_CREATE
//    functions.  Since this is a simple device driver,
//    there's really Redirect to do here by complete the requests with success.
//
//
//  INPUTS:
//
//    DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//    Irp - Address of the IRP representing the IRP_MJ_CREATE call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    STATUS_SUCCESS.   We never fail this function.        
//
//  IRQL:
//
//    This routine is called at IRQL_PASSIVE_LEVEL.
//
//  NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PENCRYPT_DEVICE_EXT    devExt = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;
    NTSTATUS                status;

#if DBG
    DbgPrint(DRIVER_NAME "Create: Starting\n");
#endif

    //
    // See if this open is to the redirector controller.  If it is, redirect the
    // request to one of our other devices.
    //
    if(devExt->Common.Redirector) {

        //
        // See if Redirection is enabled for the Driver.
        //
        if(!DoRedirect) {

            //
            // Redirection is not enabled, return an error.
            //
            status = STATUS_ACCESS_DENIED;

        } else {

#if DBG
            DbgPrint("Redirector called to Redirect Request.\n");
#endif 
            //
            // See if we can redirect the Create Request.
            //
            status = RedirectCreateRequest((PREDIRECT_CTL_EXT) devExt,Irp);

            //
            // If STATUS_REPARSE was returned, we can redirect.
            //
            if(status == STATUS_REPARSE) {
                //
                // Complete the request so that the I/O Manager will reparse
                // the request and redirect it to another device.
                //
                IoCompleteRequest(Irp,IO_DISK_INCREMENT);
                return status;
            }
        }

    } else {
        //
        // Handle the Create Request.
        //
#if DBG
        DbgPrint("%wZ Received Redirected Request\n",&devExt->DeviceName);
#endif
        status = STATUS_SUCCESS;
    }
    //
    // Redirect much to do....
    //
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

#if DBG
    DbgPrint(DRIVER_NAME "Create: Leaving\n");
#endif

    return(status);
}


///////////////////////////////////////////////////////////////////////////////
//
//  RedirectClose
//
//    This is the dispatch entry point for IRP_MJ_CLOSE functions.  Since this
//    is a simple device driver, there's really Redirect to do here by complete 
//    the requests with success.
//
//
//  INPUTS:
//
//    DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//    Irp - Address of the IRP representing the IRP_MJ_CLOSE
//          call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    STATUS_SUCCESS.   We never fail this function.        
//
//  IRQL:
//
//    This routine is called at IRQL_PASSIVE_LEVEL.
//
//  NOTES:
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PENCRYPT_DEVICE_EXT    devExt = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;

#if DBG
    DbgPrint("%wZ Close: Starting\n",&devExt->DeviceName);
#endif

    //
    // Redirect much to do....
    //
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

#if DBG
    DbgPrint("%wZ Close: Leaving\n",&devExt->DeviceName);
#endif

    return(STATUS_SUCCESS);
}

///////////////////////////////////////////////////////////////////////////////
//
//  RedirectWrite
//
//    This is the write dispatch entry point for the driver, called when the
//    I/O Manager has an IRP_MJ_WRITE request for the driver to process.
//
//  INPUTS:
//
//      DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//    Irp - Address of the IRP representing the IRP_MJ_WRITE call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    STATUS_PENDING, since we are putting the IRP on our internal queue.
//
//  IRQL:
//
//    This routine is called at IRQL_PASSIVE_LEVEL.
//
//  NOTES:
//
//    Since we set the DO_BUFFERED_IO bit in the Device Object, all buffers
//    passed to us reside in the system's non-paged pool, a pointer to this
//    buffer is in Irp->AssociatedIrp.SystemBuffer.
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
    PENCRYPT_DEVICE_EXT    devExt = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;

#if DBG
    DbgPrint("%wZ Write: Starting\n",&devExt->DeviceName);
#endif

    //
    // Complete the IRP with success
    //
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information =  ioStack->Parameters.Write.Length;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

#if DBG
    DbgPrint("%wZ Write: Leaving\n",&devExt->DeviceName);
#endif

    return(STATUS_SUCCESS);
}

///////////////////////////////////////////////////////////////////////////////
//
//  RedirectRead
//
//    This is the Read dispatch entry point for the driver, called when the
//    I/O Manager has an IRP_MJ_READ request for the driver to process.
//
//  INPUTS:
//
//    DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//    Irp - Address of the IRP representing the IRP_MJ_READ call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    STATUS_PENDING, since we are putting the IRP on our internal queue.
//
//  IRQL:
//
//    This routine is called at IRQL_PASSIVE_LEVEL.
//
//  NOTES:
//    Since we set the DO_BUFFERED_IO bit in the Device Object, all buffers
//    passed to us reside in the system's non-paged pool, a pointer to this
//    buffer is in Irp->AssociatedIrp.SystemBuffer.
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
    PENCRYPT_DEVICE_EXT    devExt = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;

#if DBG
    DbgPrint("%wZ Read: Starting\n",&devExt->DeviceName);
#endif

    //
    // Complete the IRP with success
    //
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information =  ioStack->Parameters.Read.Length;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

#if DBG
    DbgPrint("%wZ Read: Leaving\n",&devExt->DeviceName);
#endif

    return(STATUS_SUCCESS);
}


///////////////////////////////////////////////////////////////////////////////
//
//  RedirectDeviceControl
//
//    This is the dispatch entry point for processing IRP_MJ_DEVICE_CONTROL
//    Irps.  
//
//
//  INPUTS:
//
//    DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//    Irp - Address of the IRP representing the IRP_MJ_DEVICE_CONTROL call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    STATUS_SUCCESS if success, otherwise an appropriate status is returned.
//
//  IRQL:
//
//    This routine is called at IRQL_PASSIVE_LEVEL.
//
//  NOTES:
//
//    We presently only support IOCTL_OSR_REDIRECT.
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    ULONG               operation;
    PIO_STACK_LOCATION  ioStack = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS            status;
    PREDIRECT_FEATURES   pFeatures;
    PENCRYPT_DEVICE_EXT    devExt = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;

#if DBG
    DbgPrint("%wZ DeviceControl: Starting\n",&devExt->DeviceName);
#endif

    operation = ioStack->Parameters.DeviceIoControl.IoControlCode;

    //
    // Set the status field to zero for completion
    //
    Irp->IoStatus.Information = 0;

    switch (operation) {

        case IOCTL_OSR_REDIRECT:

            status = STATUS_SUCCESS;
#if DBG
            DbgPrint("%wZ DeviceControl: IOCTL_OSR_REDIRECT processed\n",&devExt->DeviceName);
#endif

            break;            

        case IOCTL_OSR_GET_REDIRECT_PROPERTY: {

            pFeatures = (PREDIRECT_FEATURES) Irp->AssociatedIrp.SystemBuffer;

#if DBG
            DbgPrint("%wZ DeviceControl: IOCTL_OSR_GET_REDIRECT_PROPERTY processed\n",&devExt->DeviceName);
#endif

            if(!Irp->AssociatedIrp.SystemBuffer || 
               (ioStack->Parameters.DeviceIoControl.OutputBufferLength < 
                    sizeof(REDIRECT_FEATURES))) {

                status = STATUS_INVALID_PARAMETER;
                break;

            }

#if DBG
            DbgPrint("%wZ DeviceControl: IOCTL_OSR_GET_REDIRECT_PROPERTY DoRedirect %d\n",&devExt->DeviceName,DoRedirect);
#endif

            pFeatures->DoRedirect = DoRedirect;

            Irp->IoStatus.Information = sizeof(REDIRECT_FEATURES);

            status = STATUS_SUCCESS;

            }
            break;

        case IOCTL_OSR_SET_REDIRECT_PROPERTY: {

            pFeatures = (PREDIRECT_FEATURES) Irp->AssociatedIrp.SystemBuffer;

#if DBG
            DbgPrint("%wZ DeviceControl: IOCTL_OSR_SET_REDIRECT_PROPERTY processed\n",&devExt->DeviceName);
#endif

            if(!Irp->AssociatedIrp.SystemBuffer || 
               (ioStack->Parameters.DeviceIoControl.InputBufferLength < 
                    sizeof(REDIRECT_FEATURES))) {

                status = STATUS_INVALID_PARAMETER;
                break;

            }

#if DBG
            DbgPrint("%wZ DeviceControl: IOCTL_OSR_SET_REDIRECT_PROPERTY DoRedirect %d\n",&devExt->DeviceName,pFeatures->DoRedirect);
#endif

            DoRedirect = pFeatures->DoRedirect;

            status = STATUS_SUCCESS;

            }
            break;


        default:

            //
            // This is the case for a bogus request status.
            //
            status = STATUS_INVALID_PARAMETER;

#if DBG
            DbgPrint("%wZ DeviceControl: Invalid IOCTL status 0x%0x\n",&devExt->DeviceName, operation);
#endif

            break;
    }

    //
    // Complete the I/O Request
    //
    Irp->IoStatus.Status = status;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

#if DBG
    DbgPrint("%wZ DeviceControl: Leaving\n",&devExt->DeviceName);
#endif

    return(status);
}





///////////////////////////////////////////////////////////////////////////////
//
//  RedirectSystemControl
//
//    This is the dispatch entry point for processing IRP_MJ_SYSTEM_CONTROL
//    Irps.  
//
//
//  INPUTS:
//
//    DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//    Irp - Address of the IRP representing the IRP_MJ_DEVICE_CONTROL call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    Propogates return status of next lower driver.
//
//  IRQL:
//
//    This routine is called at IRQL PASSIVE_LEVEL.
//
//  NOTES:
//
//	  We do not support WMI in this driver so we pass all requests down to the
//	  lower driver
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectSystemControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PENCRYPT_DEVICE_EXT    devExt = (PENCRYPT_DEVICE_EXT)DeviceObject->DeviceExtension;

#if DBG
	DbgPrint("%wZ PnP: PROCESSING IRP_MJ_SYSTEM_CONTROL\n",&devExt->DeviceName);
#endif

    //
    // Setup the I/O Stack Location
    //
    IoSkipCurrentIrpStackLocation(Irp);

    //
    // Send it down, cuz we don't care...
    //
    return(IoCallDriver(devExt->DeviceToSendIrpsTo, Irp));
}

