/*++ 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 ); } }