summaryrefslogblamecommitdiffstats
path: root/private/ntos/mailslot/read.c
blob: 407dba7da314fdeb8db6dee33f7aa4f6f326f844 (plain) (tree)































































































































































































































































































































































































                                                                                 
/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    read.c

Abstract:

    This module implements the file read routine for MSFS called by the
    dispatch driver.

Author:

    Manny Weiser (mannyw)    15-Jan-1991

Revision History:

--*/

#include "mailslot.h"

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_READ)

//
//  local procedure prototypes
//

NTSTATUS
MsCommonRead (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, MsCommonRead )
#pragma alloc_text( PAGE, MsFsdRead )
#endif

NTSTATUS
MsFsdRead (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of the NtReadFile API calls.

Arguments:

    MsfsDeviceObject - Supplies the device object to use.

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The Fsd status for the Irp

--*/

{
    NTSTATUS status;

    PAGED_CODE();
    DebugTrace(+1, Dbg, "MsFsdRead\n", 0);

    try {

        status = MsCommonRead( MsfsDeviceObject, Irp );

    } except(MsExceptionFilter( GetExceptionCode() )) {

        //
        // We had some trouble trying to perform the requested
        // operation, so we'll abort the I/O request with
        // the error status that we get back from the
        // execption code.
        //

        status = MsProcessException( MsfsDeviceObject, Irp, GetExceptionCode() );
    }

    //
    // Return to the caller.
    //

    DebugTrace(-1, Dbg, "MsFsdRead -> %08lx\n", status );

    return status;
}

NTSTATUS
MsCommonRead (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for reading a file.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - the return status for the operation

--*/

{
    NTSTATUS status;

    PIO_STACK_LOCATION irpSp;

    NODE_TYPE_CODE nodeTypeCode;
    PFCB fcb;
    PVOID fsContext2;

    PIRP readIrp;
    PUCHAR readBuffer;
    ULONG readLength;
    ULONG readRemaining;
    PDATA_QUEUE readQueue;
    ULONG messageLength;

    LARGE_INTEGER timeout;
    PKTIMER timer;
    PKDPC dpc;

    PWORK_CONTEXT workContext = NULL;
    PDATA_ENTRY dataEntry;

    PAGED_CODE();
    irpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "MsCommonRead\n", 0);
    DebugTrace( 0, Dbg, "MsfsDeviceObject = %08lx\n", (ULONG)MsfsDeviceObject);
    DebugTrace( 0, Dbg, "Irp              = %08lx\n", (ULONG)Irp);
    DebugTrace( 0, Dbg, "FileObject       = %08lx\n", (ULONG)irpSp->FileObject);

    //
    // Get the FCB and make sure that the file isn't closing.
    //

    if ((nodeTypeCode = MsDecodeFileObject( irpSp->FileObject,
                                            (PVOID *)&fcb,
                                            &fsContext2 )) == NTC_UNDEFINED) {

        DebugTrace(0, Dbg, "Mailslot is disconnected from us\n", 0);

        MsCompleteRequest( Irp, STATUS_FILE_FORCED_CLOSED );
        status = STATUS_FILE_FORCED_CLOSED;

        DebugTrace(-1, Dbg, "MsCommonRead -> %08lx\n", status );
        return status;
    }

    //
    // Allow read operations only if this is a server side handle to
    // a mailslot file.
    //

    if (nodeTypeCode != MSFS_NTC_FCB) {

        DebugTrace(0, Dbg, "FileObject is not the correct type\n", 0);

        MsDereferenceNode( (PNODE_HEADER)fcb );

        MsCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
        status = STATUS_INVALID_PARAMETER;

        DebugTrace(-1, Dbg, "MsCommonRead -> %08lx\n", status );
        return status;
    }

    //
    // Make local copies of the input parameters to make things easier, and
    // initialize the main variables that describe the read command.
    //

    readIrp        = Irp;
    readBuffer     = Irp->UserBuffer;
    readLength     = irpSp->Parameters.Read.Length;
    readRemaining  = readLength;

    readQueue = &fcb->DataQueue;


    //
    // Acquire exclusive access to the FCB.
    //

    MsAcquireExclusiveFcb( fcb );

    try {

        //
        // Ensure that this FCB still belongs to an active open mailslot.
        //

        MsVerifyFcb( fcb );

        DebugTrace(0, Dbg, "ReadBuffer     = %08lx\n", (ULONG)readBuffer);
        DebugTrace(0, Dbg, "ReadLength     = %08lx\n", readLength);
        DebugTrace(0, Dbg, "ReadQueue      = %08lx\n", (ULONG)readQueue);

        //
        // If the read queue does not contain any write entries
        // then we either need to queue this operation or
        // fail immediately.
        //

        if (!MsIsDataQueueWriters( readQueue )) {

            //
            // There are no outstanding writes.  If the read timeout is
            // non-zero queue the read IRP, otherwise fail it.
            //

            timeout = fcb->Specific.Fcb.ReadTimeout;

            if (timeout.HighPart == 0 && timeout.LowPart == 0) {

                DebugTrace(0, Dbg, "Failing read with 0 timeout\n", 0);

                MsCompleteRequest( Irp, STATUS_IO_TIMEOUT );
                status = STATUS_IO_TIMEOUT;

                DebugTrace(-1, Dbg, "MsCommonRead -> %08lx\n", status );
                try_return( NOTHING );

            } else if ( timeout.QuadPart == -1 ) {

                //
                // An infinite timeout is specified.  Just queue the IRP,
                // do not start a timer.
                //

                DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0);

                (VOID)MsAddDataQueueEntry( readQueue,
                                           ReadEntries,
                                           readLength,
                                           readIrp );

                IoMarkIrpPending( Irp );
                status = STATUS_PENDING;

            } else {

                //
                // The read timeout is non-zero.  Queue the IRP and start
                // a timer.
                //

                DebugTrace(0, Dbg, "Put the irp into the read queue\n", 0);

                dataEntry = MsAddDataQueueEntry( readQueue,
                                                 ReadEntries,
                                                 readLength,
                                                 readIrp );

                //
                // Allocate memory for the work context.
                //

                workContext = FsRtlAllocatePool( NonPagedPool,
                                                 sizeof(WORK_CONTEXT));

                timer = &workContext->Timer;
                dpc = &workContext->Dpc;

                //
                // By current definitions, timer and DPC will be aligned
                // structures.
                //

                ASSERT ( ((ULONG)timer & 3) == 0 );
                ASSERT ( ((ULONG)dpc & 3) == 0 );

                //
                // Keep a pointer to the timer, so that it can be canceled
                // if the read completes.
                //

                dataEntry->TimeoutWorkContext = workContext;

                //
                // Fill in the work context structure.
                //

                workContext->Irp = readIrp;

                ExInitializeWorkItem( &workContext->WorkItem,
                                      MsTimeoutRead,
                                      (PVOID)workContext );
                //
                // Keep a reference to the FCB to make sure it doesn't go
                // away until this IRP completes.
                //

                workContext->Fcb = fcb;

                MsAcquireGlobalLock();
                MsReferenceNode( &fcb->Header );
                MsReleaseGlobalLock();

                DebugTrace(0,
                           DEBUG_TRACE_REFCOUNT,
                           "Referencing block %08lx\n",
                           (ULONG)fcb);
                DebugTrace(0,
                           DEBUG_TRACE_REFCOUNT,
                           "    Reference count = %lx\n",
                           fcb->Header.ReferenceCount );

                //
                // Now set up a DPC and set the timer to the user specified
                // timeout.
                //

                KeInitializeTimer( timer );
                KeInitializeDpc( dpc, MsReadTimeoutHandler, workContext );
                (VOID)KeSetTimer( timer, timeout, dpc );

                IoMarkIrpPending( Irp );
                status = STATUS_PENDING;

            }

        } else {

            //
            // Otherwise we have a read IRP on a queue that contains
            // one or more write entries.  Read the data and complete
            // the read IRP.
            //

            readIrp->IoStatus = MsReadDataQueue( readQueue,
                                                 FALSE,
                                                 readBuffer,
                                                 readLength,
                                                 &messageLength
                                                );

            status = readIrp->IoStatus.Status;

            //
            // Update the file last access time and finish up the read IRP.
            //

            if ( NT_SUCCESS( status ) ) {
                KeQuerySystemTime( &fcb->Specific.Fcb.LastAccessTime );
            }

            MsCompleteRequest( readIrp, status );

        }

    try_exit: NOTHING;

    } finally {

        MsReleaseFcb( fcb );
        MsDereferenceFcb( fcb );

        DebugTrace(-1, Dbg, "MsCommonRead -> %08lx\n", status);
    }

    return status;
}