308 lines
6.9 KiB
C
308 lines
6.9 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.
|
||
|
||
|
||
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;
|
||
iosb.Information = 0;
|
||
|
||
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 (ReadLength < writeLength) {
|
||
|
||
if (Operation != Peek) {
|
||
iosb.Information = 0;
|
||
iosb.Status = STATUS_BUFFER_TOO_SMALL;
|
||
|
||
return iosb;
|
||
}
|
||
iosb.Status = STATUS_BUFFER_OVERFLOW;
|
||
|
||
DebugTrace(0, Dbg, "Overflowed peek buffer\n", 0);
|
||
|
||
amountRead = ReadLength;
|
||
} else {
|
||
amountRead = writeLength;
|
||
}
|
||
|
||
|
||
//
|
||
// Copy data from the write buffer at write offset to the
|
||
// read buffer by the mininum of write remaining or read length
|
||
//
|
||
// This copy may take an exception and thats why this call needs to be enclosed
|
||
// in try/except.
|
||
//
|
||
|
||
try {
|
||
|
||
RtlCopyMemory (ReadBuffer,
|
||
writeBuffer,
|
||
amountRead);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
iosb.Status = GetExceptionCode ();
|
||
return iosb;
|
||
|
||
}
|
||
|
||
*MessageLength = dataEntry->DataSize;
|
||
|
||
|
||
//
|
||
// If write length is larger than read length, this must be an
|
||
// overflowed peek.
|
||
//
|
||
|
||
if (writeLength <= ReadLength) {
|
||
//
|
||
// 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) {
|
||
//
|
||
// Writes don't get queued. This is an error
|
||
//
|
||
KeBugCheckEx( MAILSLOT_FILE_SYSTEM,
|
||
1,
|
||
(ULONG_PTR) writeIrp,
|
||
(ULONG_PTR) ReadQueue,
|
||
(ULONG_PTR) dataEntry );
|
||
|
||
}
|
||
}
|
||
|
||
DebugTrace(0, Dbg, "Successful mailslot read\n", 0);
|
||
|
||
//
|
||
// Indicate success.
|
||
//
|
||
|
||
iosb.Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
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 PDEVICE_OBJECT DeviceObject,
|
||
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;
|
||
|
||
fcb = workContext->Fcb;
|
||
|
||
dataQueue = &fcb->DataQueue;
|
||
//
|
||
// Acquire exclusive access to the FCB. This must succeed.
|
||
//
|
||
|
||
MsAcquireExclusiveFcb( fcb );
|
||
|
||
//
|
||
// There are two cases to consider here. Either this timer is the first completion
|
||
// event for this IRP or we werent but we started running before they could cancel the timer.
|
||
// When the second case is detected the other thread NULL's out the IRP pointer.
|
||
//
|
||
irp = workContext->Irp;
|
||
if (irp) {
|
||
|
||
|
||
dataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( irp );
|
||
//
|
||
// Nobody else should touch this once we release the lock.
|
||
//
|
||
dataEntry->TimeoutWorkContext = NULL;
|
||
|
||
//
|
||
// If cancel isn't active for the IRP.
|
||
//
|
||
|
||
irp = MsRemoveDataQueueEntry( dataQueue, dataEntry );
|
||
}
|
||
|
||
|
||
//
|
||
// Release the FCB, and derefernce it.
|
||
//
|
||
|
||
MsReleaseFcb( fcb );
|
||
MsDereferenceFcb( fcb );
|
||
|
||
//
|
||
// Free the work context and the work item. We have to do this unconditionaly
|
||
// if we started running
|
||
//
|
||
IoFreeWorkItem (workContext->WorkItem);
|
||
ExFreePool( workContext );
|
||
|
||
if (irp != NULL) {
|
||
DebugTrace(0, Dbg, "Completing IRP %p\n", irp );
|
||
|
||
MsCompleteRequest( irp, STATUS_IO_TIMEOUT );
|
||
}
|
||
}
|