303 lines
6.8 KiB
C
303 lines
6.8 KiB
C
/*++
|
||
|
||
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 );
|
||
|
||
}
|