You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
7.2 KiB
307 lines
7.2 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
}
|