summaryrefslogblamecommitdiffstats
path: root/private/ntos/mailslot/readsup.c
blob: f89f16f2edebbf082cbbc577dfaca21649a7d875 (plain) (tree)













































































































































































































































































































                                                                                 
/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    readsup.c

Abstract:

    This module implements the read support routine.  This is a common
    read function that is called to do read and peek.

Author:

    Manny Weiser (mannyw)    15-Jan-1991

Revision History:

--*/

#include "mailslot.h"

//
// The debug trace level
//

#define Dbg                              (DEBUG_TRACE_READSUP)

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, MsReadDataQueue )
#pragma alloc_text( PAGE, MsTimeoutRead )
#endif

IO_STATUS_BLOCK
MsReadDataQueue (
    IN PDATA_QUEUE ReadQueue,
    IN ENTRY_TYPE Operation,
    IN PUCHAR ReadBuffer,
    IN ULONG ReadLength,
    OUT PULONG MessageLength
    )

/*++

Routine Description:

    This function reads data from the read queue and fills up the
    read buffer.  It will also dequeue the data entry if this is not
    a peek operation.

    It will only be called if there is at least one message to read.

    *** This function must be called from within a try statement.

Arguments:

    ReadQueue - Provides the read queue to examine.  Its state must
        already be set to WriteEntries.

    Operation - Indicates the type of operation to perform.  If the
        operation is Peek, the write data entry is not dequeued.

    ReadBuffer - Supplies a buffer to receive the data

    ReadLength - Supplies the length, in bytes, of ReadBuffer.

    MessageLength - Returns the full size of the message, even if the
        read buffer is not large enough to contain the entire message.

Return Value:

    IO_STATUS_BLOCK - Indicates the result of the operation.

--*/

{
    IO_STATUS_BLOCK iosb;

    PLIST_ENTRY listEntry;
    PDATA_ENTRY dataEntry;
    PFCB fcb;

    PUCHAR writeBuffer;
    ULONG writeLength;

    ULONG amountRead;

    PAGED_CODE();
    DebugTrace(+1, Dbg, "MsReadDataQueue\n", 0);
    DebugTrace( 0, Dbg, "ReadQueue     = %08lx\n", (ULONG)ReadQueue);
    DebugTrace( 0, Dbg, "Operation     = %08lx\n", Operation);
    DebugTrace( 0, Dbg, "ReadBuffer    = %08lx\n", (ULONG)ReadBuffer);
    DebugTrace( 0, Dbg, "ReadLength    = %08lx\n", ReadLength);

    //
    // Read the first message out of the data queue.
    //

    iosb.Status = STATUS_SUCCESS;

    listEntry = MsGetNextDataQueueEntry( ReadQueue );
    ASSERT( listEntry != &ReadQueue->DataEntryList );

    dataEntry = CONTAINING_RECORD( listEntry, DATA_ENTRY, ListEntry );

    //
    // Calculate how much data is in this entry.
    //

    writeBuffer = dataEntry->DataPointer;
    writeLength = dataEntry->DataSize;

    DebugTrace(0, Dbg, "WriteBuffer    = %08lx\n", (ULONG)writeBuffer);
    DebugTrace(0, Dbg, "WriteLength    = %08lx\n", writeLength);

    //
    // Fail this operation, if it is a read and the buffer is not large
    // enough.
    //

    if ( (Operation != Peek) && (ReadLength < writeLength) ) {

        iosb.Information = 0;
        iosb.Status = STATUS_BUFFER_TOO_SMALL;

        return iosb;

    }

    //
    // Copy data from the write buffer at write offset to the
    // read buffer by the mininum of write remaining or read length
    //

    RtlMoveMemory( ReadBuffer,
                   writeBuffer,
                   writeLength );

    *MessageLength = dataEntry->DataSize;


    //
    // If write length is larger than read length, this must be an
    // overflowed peek.
    //

    if ( writeLength > ReadLength) {

        DebugTrace(0, Dbg, "Overflowed peek buffer\n", 0);

        iosb.Status = STATUS_BUFFER_OVERFLOW;

    } else {

        //
        // The write entry is done so remove it from the read
        // queue, if this is not a peek operation.  This might
        // also have an IRP that needs to be completed.
        //

        if (Operation != Peek) {

            PIRP writeIrp;

            if ((writeIrp = MsRemoveDataQueueEntry( ReadQueue,
                                                    dataEntry )) != NULL) {

                //
                // Update the last modification time.
                //

                fcb = CONTAINING_RECORD( ReadQueue, FCB, DataQueue );
                KeQuerySystemTime( &fcb->Specific.Fcb.LastModificationTime );

                MsCompleteRequest( writeIrp, STATUS_SUCCESS );
            }
        }

        DebugTrace(0, Dbg, "Successful mailslot read\n", 0);

        //
        // Indicate success.
        //

        iosb.Status = STATUS_SUCCESS;
    }

    amountRead = writeLength < ReadLength ? writeLength : ReadLength;

    DebugTrace(0, Dbg, "Amount read = %08lx\n", amountRead);

    iosb.Information = amountRead;
    DebugTrace(-1, Dbg, "MsReadDataQueue -> iosb.Status = %08lx\n", iosb.Status);
    return iosb;
}


VOID
MsTimeoutRead (
    IN PVOID Context
    )

/*++

Routine Description:

    This routine times out a read operation.  It gains exclusive
    access to the FCB, and searches the data queue of read operations.

    If the timed out read operation is not found, it is assumed that
    a write IRP completed the read after the time out DPC ran, but
    before this function could complete the read IRP.

Arguments:

    Context - a pointer to our WorkContext

Return Value:

    None.

--*/

{
    PDATA_QUEUE dataQueue;
    PLIST_ENTRY listEntry;
    PIRP queuedIrp;
    PDATA_ENTRY dataEntry;
    PWORK_CONTEXT workContext;
    PIRP irp;
    PFCB fcb;

    PAGED_CODE();

    //
    // Reference our local variables.
    //

    workContext = (PWORK_CONTEXT)Context;

    irp = workContext->Irp;
    fcb = workContext->Fcb;

    //
    // Acquire exclusive access to the FCB.  This must succeed.
    //

    MsAcquireExclusiveFcb( fcb );

    //
    // Walk the FCB's data queue, looking for this read IRP.
    //

    dataQueue = &fcb->DataQueue;

    for (listEntry = MsGetNextDataQueueEntry( dataQueue );
         listEntry != &dataQueue->DataEntryList;
         listEntry = listEntry->Flink ) {

         //
         // This is an outstanding read request from this FCB.
         // If it is the read request that has timed out, then
         // complete it with a timed out status.
         //

         dataEntry = CONTAINING_RECORD( listEntry, DATA_ENTRY, ListEntry );
         queuedIrp = dataEntry->Irp;

         if ( queuedIrp == irp ) {

             //
             // The timed out IRP is still queued.  Dequeue it and complete
             // it now.
             //

             MsRemoveDataQueueEntry( dataQueue, dataEntry );
             DebugTrace(0, Dbg, "Completing IRP %08lx\n", (ULONG)queuedIrp );

             MsCompleteRequest( queuedIrp, STATUS_IO_TIMEOUT );

             break;
         }

    }

    //
    // Free the work context structure.  Note that we have to free this
    // whenever this routine runs, even if the I/O request was cancelled,
    // because the canceller didn't free it.
    //

    ExFreePool( workContext );

    //
    // Release the FCB, and derefernce it.
    //

    MsReleaseFcb( fcb );
    MsDereferenceFcb( fcb );

}