WindowsXP-SP1/base/fs/mailslot/readsup.c

308 lines
7.2 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
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 );
}
}