/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    dispatch.c

Abstract:

    This module contains the dispatch routines for
    ws2ifsl.sys driver

Author:

    Vadim Eydelman (VadimE)    Dec-1996

Revision History:
    Vadim Eydelman (VadimE)    Oct-1997, rewrite to properly handle IRP
                                        cancellation

--*/

#include "precomp.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, DispatchCreate)
#pragma alloc_text (PAGE, DispatchCleanup)
#pragma alloc_text (PAGE, DispatchClose)
#pragma alloc_text (PAGE, DispatchReadWrite)
#pragma alloc_text (PAGE, DispatchDeviceControl)
#pragma alloc_text (PAGE, FastIoDeviceControl)
#endif

NTSTATUS
DispatchCreate (
	IN PDEVICE_OBJECT 	DeviceObject,
	IN PIRP 			Irp
	)
/*++

Routine Description:

    This routine is called as the result of a request to create
    a file associated with WS2IFSL driver device object.

Arguments:

    DeviceObject - WS2IFSL device object
    Irp          - create Irp

Return Value:

    STATUS_SUCCESS - requested file object can be created
    STATUS_INVALID_PARAMETER - required extened attribute is missing
                        or invalid
    STATUS_INSUFFICIENT_RESOURCES - not enough resources to complete
                        this request

--*/
{
    NTSTATUS                    status;
    PIO_STACK_LOCATION          irpSp;
    PFILE_FULL_EA_INFORMATION   eaBuffer;

    PAGED_CODE ();

    // Get extended attribute buffer which identifies the
    // type of file that should be created.

    eaBuffer = Irp->AssociatedIrp.SystemBuffer;
    if (eaBuffer!=NULL) {
        irpSp = IoGetCurrentIrpStackLocation (Irp);
        if ((eaBuffer->EaNameLength==WS2IFSL_SOCKET_EA_NAME_LENGTH)
                && (strcmp (eaBuffer->EaName, WS2IFSL_SOCKET_EA_NAME)==0)) {
            // This is the request to create socket file

            status = CreateSocketFile (irpSp->FileObject,
                                        Irp->RequestorMode,
                                        eaBuffer);
        }
        else if ((eaBuffer->EaNameLength==WS2IFSL_PROCESS_EA_NAME_LENGTH)
                && (strcmp (eaBuffer->EaName, WS2IFSL_PROCESS_EA_NAME)==0)) {
            // This is the request to create process file

            status = CreateProcessFile (irpSp->FileObject,
                                        Irp->RequestorMode,
                                        eaBuffer);
        }
        else
            status = STATUS_INVALID_PARAMETER;
    }
    else
        status = STATUS_INVALID_PARAMETER;
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);

    return status;
} // DispatchCreate

NTSTATUS
DispatchCleanup (
	IN PDEVICE_OBJECT 	DeviceObject,
	IN PIRP 			Irp
	)
/*++

Routine Description:

    This routine is called when all handles to a file associated with WS2IFSL
    driver device object are closed, so the driver should cleanup all the
    outstanding IRPs.

Arguments:

    DeviceObject - WS2IFSL device object
    Irp          - cleanup Irp

Return Value:

    STATUS_SUCCESS - cleanup operation completed
    STATUS_PENDING - cleanup operation started and IoCompleteRequest will be
                    called when it is done

--*/
{
    NTSTATUS                    status;
    PIO_STACK_LOCATION          irpSp;
    ULONG                       eaNameTag;

    PAGED_CODE ();

    // Get the file type from file object context
    irpSp = IoGetCurrentIrpStackLocation (Irp);
    eaNameTag = *((PULONG)irpSp->FileObject->FsContext);

    // Call appropriate routine based on file type
    switch (eaNameTag) {
    case SOCKET_FILE_EANAME_TAG:
        status = CleanupSocketFile (irpSp->FileObject, Irp);
        break;
    case PROCESS_FILE_EANAME_TAG:
        status = CleanupProcessFile (irpSp->FileObject, Irp);
        break;
    default:
        ASSERTMSG ("Unknown file EA name tag", FALSE);
        status = STATUS_INVALID_HANDLE;
        break;
    }

    // Complete the request if it was not marked pending
    if (status!=STATUS_PENDING) {
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
    }

    return status;
} // DispatchCleanup

NTSTATUS
DispatchClose (
	IN PDEVICE_OBJECT 	DeviceObject,
	IN PIRP 			Irp
	)
/*++

Routine Description:

    This routine is called when all references to a file associated with WS2IFSL
    driver device object are released and IO system is about to delete the
    file object itself

Arguments:

    DeviceObject - WS2IFSL device object
    Irp          - close Irp

Return Value:

    STATUS_SUCCESS - close operation completed

--*/
{
    NTSTATUS                    status;
    PIO_STACK_LOCATION          irpSp;
    ULONG                       eaNameTag;

    PAGED_CODE ();

    // Get the file type from file object context
    irpSp = IoGetCurrentIrpStackLocation (Irp);
    eaNameTag = *((PULONG)irpSp->FileObject->FsContext);

    // Call appropriate routine based on file type
    switch (eaNameTag) {
    case SOCKET_FILE_EANAME_TAG:
        CloseSocketFile (irpSp->FileObject);
        status = STATUS_SUCCESS;
        break;
    case PROCESS_FILE_EANAME_TAG:
        CloseProcessFile (irpSp->FileObject);
        status = STATUS_SUCCESS;
        break;
    default:
        ASSERTMSG ("Unknown file EA name tag", FALSE);
        status = STATUS_INVALID_HANDLE;
        break;
    }

    // Complete the request
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest (Irp, IO_NO_INCREMENT);

    return status;
} // DispatchClose

NTSTATUS
DispatchReadWrite (
	IN PDEVICE_OBJECT 	DeviceObject,
	IN PIRP 			Irp
	)
/*++

Routine Description:

    This routine is called to perform read or write operation on file object.
    It is only supported for socket files.

Arguments:

    DeviceObject - WS2IFSL device object
    Irp          - read/write  Irp

Return Value:

    STATUS_PENDING - operation is passed onto WS2IFSL DLL to execute
    STATUS_CANCELED - operation canceled because WS2IFSL DLL has been unloaded
    STATUS_INVALID_DEVICE_REQUEST - the operation cannot be performed on
                        this file object.
    STATUS_INVALID_HANDLE - file object is not valid in the context of
                        current process


--*/
{
    NTSTATUS                    status;
    PIO_STACK_LOCATION          irpSp;
    ULONG                       eaNameTag;

    PAGED_CODE ();

    // Get the file type from file object context
    irpSp = IoGetCurrentIrpStackLocation (Irp);
    eaNameTag = *((PULONG)irpSp->FileObject->FsContext);

    // Call appropriate routine based on file type
    switch (eaNameTag) {
    case SOCKET_FILE_EANAME_TAG:
        status = DoSocketReadWrite (irpSp->FileObject, Irp);
        break;
    default:
        ASSERTMSG ("Unknown file EA name tag", FALSE);
    case PROCESS_FILE_EANAME_TAG:
        // This operation is not valid for process files
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    // Complete the request if it was not marked pending
    if (status!=STATUS_PENDING) {
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
    }

    return status;
} // DispatchReadWrite


NTSTATUS
DispatchDeviceControl (
	IN PDEVICE_OBJECT 	DeviceObject,
	IN PIRP 			Irp
	)
/*++

Routine Description:

    This routine is called to perform device control operation on file object.

Arguments:

    DeviceObject - WS2IFSL device object
    Irp          - device control Irp

Return Value:

    STATUS_SUCCESS - device control operation completed
    STATUS_PENDING - operation is in progress
    STATUS_INVALID_DEVICE_REQUEST - the operation cannot be performed on
                        this file object.
    STATUS_INVALID_HANDLE - file object is not valid in the context of
                        current process

--*/
{
    NTSTATUS                    status;
    PIO_STACK_LOCATION          irpSp;
    ULONG                       eaNameTag;
    ULONG                       function;

    PAGED_CODE ();

    // Get the file type from file object context
    irpSp = IoGetCurrentIrpStackLocation (Irp);
    eaNameTag = *((PULONG)irpSp->FileObject->FsContext);

    // Call appropriate routine based on file type
    switch (eaNameTag) {
    case SOCKET_FILE_EANAME_TAG:
        function = WS2IFSL_IOCTL_FUNCTION(SOCKET,irpSp->Parameters.DeviceIoControl.IoControlCode);
        if ((function<sizeof(SocketIoControlMap)/sizeof(SocketIoControlMap[0])) &&
                (SocketIoctlCodeMap[function]==irpSp->Parameters.DeviceIoControl.IoControlCode)) {
            // Use table dispatch to call appropriate internal IOCTL routine
            ASSERTMSG ("Socket file device control requests should have been handled"
                    " by FastIo path ", FALSE);
            SocketIoControlMap[function] (
                    irpSp->FileObject,
                    Irp->RequestorMode,
                    irpSp->Parameters.DeviceIoControl.Type3InputBuffer,
                    irpSp->Parameters.DeviceIoControl.InputBufferLength,
                    Irp->UserBuffer,
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength,
                    &Irp->IoStatus);
            status = Irp->IoStatus.Status;
        }
        else if ((irpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_SEND_DATAGRAM)
                    || (irpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_RECEIVE_DATAGRAM)
                    || (irpSp->Parameters.DeviceIoControl.IoControlCode==IOCTL_AFD_RECEIVE))
            // Handle some "popular" afd IOCTLs
            status = DoSocketAfdIoctl (irpSp->FileObject, Irp);
        else {
            WsPrint (DBG_FAILURES,
                ("WS2IFSL-%04lx DispatchDeviceControl: Unsupported IOCTL - %lx!!!\n",
                    PsGetCurrentProcessId(),
                    irpSp->Parameters.DeviceIoControl.IoControlCode
                    ));
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
        break;
    case PROCESS_FILE_EANAME_TAG:
        function = WS2IFSL_IOCTL_FUNCTION(PROCESS,irpSp->Parameters.DeviceIoControl.IoControlCode);
        if ((function<sizeof(ProcessIoControlMap)/sizeof(ProcessIoControlMap[0])) &&
                (ProcessIoctlCodeMap[function]==irpSp->Parameters.DeviceIoControl.IoControlCode)) {
            // Use table dispatch to call appropriate internal IOCTL routine
            ASSERTMSG ("Process file device control requests should have been handled"
                    " by FastIo path ", FALSE);
            ProcessIoControlMap[function] (
                    irpSp->FileObject,
                    Irp->RequestorMode,
                    irpSp->Parameters.DeviceIoControl.Type3InputBuffer,
                    irpSp->Parameters.DeviceIoControl.InputBufferLength,
                    Irp->UserBuffer,
                    irpSp->Parameters.DeviceIoControl.OutputBufferLength,
                    &Irp->IoStatus);
            status = Irp->IoStatus.Status;
        }
        else {
            WsPrint (DBG_FAILURES,
                ("WS2IFSL-%04lx DispatchDeviceControl: Unsupported IOCTL - %lx!!!\n",
                    PsGetCurrentProcessId(),
                    irpSp->Parameters.DeviceIoControl.IoControlCode
                    ));
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
        break;
    default:
        ASSERTMSG ("Unknown file EA name tag", FALSE);
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    // Complete the request if it was not marked pending
    if (status!=STATUS_PENDING) {
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
    }

    return status;
} // DispatchDeviceControl


BOOLEAN
FastIoDeviceControl (
	IN PFILE_OBJECT 		FileObject,
	IN BOOLEAN 			    Wait,
	IN PVOID 				InputBuffer	OPTIONAL,
	IN ULONG 				InputBufferLength,
	OUT PVOID 				OutputBuffer	OPTIONAL,
	IN ULONG 				OutputBufferLength,
	IN ULONG 				IoControlCode,
	OUT PIO_STATUS_BLOCK	IoStatus,
	IN PDEVICE_OBJECT 		DeviceObject
    )
/*++

Routine Description:

    This routine is called to perform device control operation on file object.
    This is IO system fast path that assumes immediate action

Arguments:

    FileObject      - file object to which request is directed;
    Wait            - ??? (always TRUE);
    InputBuffer     - address of the input buffer;
    InputBufferLength - size of the input buffer;
    OutputBuffer    - address of the output buffer;
    OutputBufferLength - size of the output buffer;
    IoControlCode   - code of the operation to be performed;
    IoStatus        - status of the operation returned by the driver:
    DeviceObject    - WS2IFSL device object

Return Value:

    TRUE    - operation completed,
    FALSE   - operation should be preformed using Irps
--*/
{
    BOOLEAN         done = FALSE;
    ULONG           eaNameTag;
    ULONG           function;

    PAGED_CODE ();

    // Get the file type from file object context
    eaNameTag = *((PULONG)FileObject->FsContext);

    // Call appropriate routine based on file type
    switch (eaNameTag) {
    case SOCKET_FILE_EANAME_TAG:
        function = WS2IFSL_IOCTL_FUNCTION(SOCKET,IoControlCode);
        if ((function<sizeof(SocketIoControlMap)/sizeof(SocketIoControlMap[0])) &&
                (SocketIoctlCodeMap[function]==IoControlCode)) {
            IO_STATUS_BLOCK IoStatusTemp;

            // Use table dispatch to call appropriate internal IOCTL routine
            SocketIoControlMap[function] (
                    FileObject,
                    ExGetPreviousMode(),
                    InputBuffer,
                    InputBufferLength,
                    OutputBuffer,
                    OutputBufferLength,
                    &IoStatusTemp);

            if (NT_SUCCESS(IoStatusTemp.Status))
            {
                done = TRUE;
                memcpy(IoStatus, &IoStatusTemp, sizeof(IoStatusTemp));
            }
        }
        else if ((IoControlCode==IOCTL_AFD_SEND_DATAGRAM)
                    || (IoControlCode==IOCTL_AFD_RECEIVE_DATAGRAM)
                    || (IoControlCode==IOCTL_AFD_RECEIVE))
            // AFD ioctls can only be handled on "slow" io path (need IRP)
            NOTHING;
        else {
            WsPrint (DBG_FAILURES,
                ("WS2IFSL-%04lx FastIoDeviceControl: Unsupported IOCTL - %lx!!!\n",
                    PsGetCurrentProcessId(), IoControlCode));
            //
            // Let the real dispatch deal with the error.
            NOTHING;
        }
        break;
    case PROCESS_FILE_EANAME_TAG:
        function = WS2IFSL_IOCTL_FUNCTION(PROCESS,IoControlCode);
        if ((function<sizeof(ProcessIoControlMap)/sizeof(ProcessIoControlMap[0])) &&
                (ProcessIoctlCodeMap[function]==IoControlCode)) {
            // Use table dispatch to call appropriate internal IOCTL routine
            IO_STATUS_BLOCK IoStatusTemp;

            ProcessIoControlMap[function] (
                    FileObject,
                    ExGetPreviousMode(),
                    InputBuffer,
                    InputBufferLength,
                    OutputBuffer,
                    OutputBufferLength,
                    &IoStatusTemp);

            if (NT_SUCCESS(IoStatusTemp.Status))
            {
                done = TRUE;
                memcpy(IoStatus, &IoStatusTemp, sizeof(IoStatusTemp));
            }
        }
        else {
            WsPrint (DBG_FAILURES,
                ("WS2IFSL-%04lx FastIoDeviceControl: Unsupported IOCTL - %lx!!!\n",
                    PsGetCurrentProcessId(),IoControlCode));
            //
            // Let the real dispatch deal with the error.
            NOTHING;
        }
        break;
    default:
        ASSERTMSG ("Unknown file EA name tag", FALSE);
        //
        // Let the real dispatch deal with the error.
        NOTHING;
        break;
    }

    return done;
} // FastIoDeviceControl

NTSTATUS
DispatchPnP (
	IN PDEVICE_OBJECT 	DeviceObject,
	IN PIRP 			Irp
	)
/*++

Routine Description:

    This routine is called to perform PnP control operation on file object.

Arguments:

    DeviceObject - WS2IFSL device object
    Irp          - PnP Irp

Return Value:

    STATUS_SUCCESS - device control operation completed
    STATUS_PENDING - operation is in progress
    STATUS_INVALID_DEVICE_REQUEST - the operation cannot be performed on
                        this file object.

--*/
{
    NTSTATUS                    status;
    PIO_STACK_LOCATION          irpSp;
    ULONG                       eaNameTag;

    PAGED_CODE ();

    // Get the file type from file object context
    irpSp = IoGetCurrentIrpStackLocation (Irp);
    eaNameTag = *((PULONG)irpSp->FileObject->FsContext);

    // Call appropriate routine based on file type
    switch (eaNameTag) {
    case SOCKET_FILE_EANAME_TAG:
        switch (irpSp->MinorFunction) {
        case IRP_MN_QUERY_DEVICE_RELATIONS:
            status = SocketPnPTargetQuery (irpSp->FileObject, Irp);
            break;
        default:
            status = STATUS_INVALID_DEVICE_REQUEST;
            break;
        }
        break;

    default:
        ASSERTMSG ("Unknown file EA name tag", FALSE);

    case PROCESS_FILE_EANAME_TAG:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
   }
    // Complete the request if it was not marked pending
    if (status!=STATUS_PENDING) {
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
    }

    return status;
}