///////////////////////////////////////////////////////////////////////////////
//
//    (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:
//
//        RedirectCtrl.Cpp
//
//    ABSTRACT:
//
//      This file contains the code which deals with keep track of
//      registered devices and contains the code that redirects create
//      requests to those registered devices.
//
//    AUTHOR(S):
//
//        OSR Open Systems Resources, Inc.
// 
//    REVISION:   
//
//
///////////////////////////////////////////////////////////////////////////////

#include "redirect.h"

///////////////////////////////////////////////////////////////////////////////
//
//  RegisterWithRedirect
//
//    This routine registers the input device with the Controller.  The Controller
//    links the device into the list of registered devices.
//
//
//  INPUTS:
//
//    DevExt - Address of the device extension for the registering device.
//  
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      None.
//
//  IRQL:
//
//    This routine is called at IRQL PASSIVE_LEVEL.
//
//  NOTES:
//
//	  If the device was previously registered, it is not added to the list.
//
///////////////////////////////////////////////////////////////////////////////
void RegisterWithRedirect(PENCRYPT_DEVICE_EXT DevExt)
{
    PREDIRECT_CTL_EXT ctlExt = (PREDIRECT_CTL_EXT) 
                     DevExt->Common.DriverObjectExt->RedirectorDeviceObject->DeviceExtension;

    //
    //  Acquire the List Mutex.
    //
    KeEnterCriticalRegion();
    ExAcquireFastMutex(&ctlExt->DeviceNameListLock);

    //
    // Make sure that the device is not registered already.  If it is, get out of here.
    //
    if(DevExt->Registered) {
        ExReleaseFastMutex(&ctlExt->DeviceNameListLock);
        KeLeaveCriticalRegion();
        return;
    }

    //
    // Add the new entry to the end of the list.  
    //
    InsertTailList(&ctlExt->DeviceNameList,&DevExt->RegisteredListEntry);

    //
    // Set the registered flag in the Devices Extension.
    //
    DevExt->Registered = TRUE;

    //
    // Increment the count of registered devices.  
    //
    ctlExt->DeviceNameListCount++;

    //
    // Release the List Mutex.
    //
    ExReleaseFastMutex(&ctlExt->DeviceNameListLock);
    KeLeaveCriticalRegion();
}

///////////////////////////////////////////////////////////////////////////////
//
//  UnregisterWithRedirect
//
//    This routine unregisters the input device with the Controller.  The Controller
//    unlinks the device from the list of registered devices.
//
//
//  INPUTS:
//
//    DevExt - Address of the device extension for the unregistering device.
//  
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      None.
//
//  IRQL:
//
//    This routine is called at IRQL PASSIVE_LEVEL.
//
//  NOTES:
//
//	  If the device was not previously registered, it is not removed from
//    the list.
//
///////////////////////////////////////////////////////////////////////////////
void UnregisterWithRedirect(PENCRYPT_DEVICE_EXT DevExt)
{
    PREDIRECT_CTL_EXT ctlExt = (PREDIRECT_CTL_EXT) 
                 DevExt->Common.DriverObjectExt->RedirectorDeviceObject->DeviceExtension;

    //
    // Acquire the mutex that is used to synchronize access to the list.
    //
    KeEnterCriticalRegion();
    ExAcquireFastMutex(&ctlExt->DeviceNameListLock);

    //
    // Make sure that the is registered, if not get out of here.
    //
    if(!DevExt->Registered) {
        ExReleaseFastMutex(&ctlExt->DeviceNameListLock);
        KeLeaveCriticalRegion();
        return;
    }

    //
    // Remove the entry from the list and decrement the number of registered
    // devices.
    //
    RemoveEntryList(&DevExt->RegisteredListEntry);
    DevExt->Registered = FALSE;
    ctlExt->DeviceNameListCount--;

    //
    // See if this is the last device registered.
    //
    if(!ctlExt->DeviceNameListCount) {
        UNICODE_STRING linkName;

        //
        // This is the last device registered.  Delete the Symbolic Link.
        //
        RtlInitUnicodeString(&linkName, L"\\DosDevices\\Redirect");

        IoDeleteSymbolicLink(&linkName);

        //
        // Release the Mutex.
        //
        ExReleaseFastMutex(&ctlExt->DeviceNameListLock);
        KeLeaveCriticalRegion();

        //
        // Delete the Controller Device Object.
        //
        IoDeleteDevice(ctlExt->Common.DriverObjectExt->RedirectorDeviceObject);

    } else {

        //
        // This is not the last device, so just release the Mutex.
        //
        ExReleaseFastMutex(&ctlExt->DeviceNameListLock);
        KeLeaveCriticalRegion();
    }
}

///////////////////////////////////////////////////////////////////////////////
//
//  RedirectCreateRequest
//
//    This routine is responsible for redirecting create request to a registered
//    device based on a round robin algorithm.
//
//
//  INPUTS:
//
//    CtlExt - Address of the controller device extension.
//    Irp - Address of Create Irp to redirect.
//  
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      STATUS_REPARSE if the IRP is to be redirected, otherwise
//      another status is returned.
//
//  IRQL:
//
//    This routine is called at IRQL PASSIVE_LEVEL.
//
//  NOTES:
//
//	  If no devices were registered, the Irp cannot be redirected.
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS RedirectCreateRequest(PREDIRECT_CTL_EXT CtlExt,PIRP Irp)
{
    ULONG index = 0;
    PLIST_ENTRY pEntry;
    PENCRYPT_DEVICE_EXT devExt = NULL;
    PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
    PWCHAR pBuffer = NULL;

    //
    // Wait for the List Mutex.
    //
    KeEnterCriticalRegion();
    ExAcquireFastMutex(&CtlExt->DeviceNameListLock);

    //
    // See if there are any devices contained with in list.
    //
    if(IsListEmpty(&CtlExt->DeviceNameList)) {

        //
        // No devices were registered, so we must return an error.
        //
        ExReleaseFastMutex(&CtlExt->DeviceNameListLock);
        KeLeaveCriticalRegion();
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Determine if our next device index exceeds the number of entries
    // in the list.  If so, we reset the index back to 0.
    //
    if(CtlExt->DeviceNameListIndex >= CtlExt->DeviceNameListCount) {
        CtlExt->DeviceNameListIndex = 0;
    }
    
    //
    // Find the entry in the list to process.   Essentially we just
    // count entries until we hit our selected index.
    //
    pEntry = CtlExt->DeviceNameList.Flink;
    while(index < CtlExt->DeviceNameListIndex) {
        pEntry = pEntry->Flink;
        index++;
    }

    //
    // Increment the index so that the next time this routine is called
    // we pick the next entry in the list.
    //
    CtlExt->DeviceNameListIndex++;

    //
    // Calculate the beginning of the device extension 
    //
    devExt = (PENCRYPT_DEVICE_EXT) 
                CONTAINING_RECORD(pEntry,ENCRYPT_DEVICE_EXT,RegisteredListEntry);

    //
    // Allocate a paged pool buffer to hold the name of the device that we are
    // redirecting the create to.
    //
    pBuffer = (PWCHAR) ExAllocatePoolWithTag(PagedPool,devExt->DeviceName.Length,'mnDR');

    //
    // Make sure that we have a buffer.  If not we cannot redirect the request.
    //
    if(!pBuffer) {
        //
        // No buffer allocated, so we have to return an error.
        //
        ExReleaseFastMutex(&CtlExt->DeviceNameListLock);
        KeLeaveCriticalRegion();
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Copy the device name to the allocated buffer
    //
    RtlCopyMemory(pBuffer,devExt->DeviceName.Buffer,devExt->DeviceName.Length);

    //
    // Fix up the UNICODE_STRING fields in the FILE_OBJECT for the new name.
    //
    ioStack->FileObject->FileName.Length = devExt->DeviceName.Length;
    ioStack->FileObject->FileName.MaximumLength = devExt->DeviceName.Length;

    //
    // Delete the buffer used in the FILE_OBJECT file name if it exists.
    //
    if(ioStack->FileObject->FileName.Buffer) {
        ExFreePool(ioStack->FileObject->FileName.Buffer);
    }

    //
    // Put our allocated buffer in the FILE_OBJECT File Name.  This now
    // contains the name of the device that this create is going to be
    // redirected to.
    //
    ioStack->FileObject->FileName.Buffer = pBuffer;

    DbgPrint("RedirectCreateRequest to %wZ\n",&devExt->DeviceName);

    //
    // Tell the IO Manager that the name needs to be reparsed.
    //
    Irp->IoStatus.Status = STATUS_REPARSE;
    Irp->IoStatus.Information = IO_REPARSE;
      
    //
    // Release our mutex.
    //
    ExReleaseFastMutex(&CtlExt->DeviceNameListLock);
    KeLeaveCriticalRegion();

    return STATUS_REPARSE;
}
