859 lines
21 KiB
C
859 lines
21 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
DataSup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the mailslot data queue support functions.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Manny Weiser (mannyw) 9-Jan-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "mailslot.h"
|
|||
|
|
|||
|
//
|
|||
|
// The debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_DATASUP)
|
|||
|
|
|||
|
//
|
|||
|
// Local declarations
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
MsSetCancelRoutine(
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// The following macro is used to dump a data queue
|
|||
|
//
|
|||
|
|
|||
|
#define DumpDataQueue(S,P) { \
|
|||
|
ULONG MsDumpDataQueue(IN ULONG Level, IN PDATA_QUEUE Ptr); \
|
|||
|
DebugTrace(0,Dbg,S,0); \
|
|||
|
DebugTrace(0,Dbg,"", MsDumpDataQueue(Dbg,P)); \
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, MsAddDataQueueEntry )
|
|||
|
#pragma alloc_text( PAGE, MsInitializeDataQueue )
|
|||
|
#pragma alloc_text( PAGE, MsRemoveDataQueueEntry )
|
|||
|
#pragma alloc_text( PAGE, MsUninitializeDataQueue )
|
|||
|
#pragma alloc_text( PAGE, MsSetCancelRoutine )
|
|||
|
#pragma alloc_text( PAGE, MsResetCancelRoutine )
|
|||
|
#pragma alloc_text( PAGE, MsRemoveDataQueueIrp )
|
|||
|
#endif
|
|||
|
|
|||
|
#if 0
|
|||
|
NOT PAGEABLE -- MsCancelDataQueueIrp
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MsInitializeDataQueue (
|
|||
|
IN PDATA_QUEUE DataQueue,
|
|||
|
IN PEPROCESS Process,
|
|||
|
IN ULONG Quota,
|
|||
|
IN ULONG MaximumMessageSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initializes a new data queue. The indicated quota is taken
|
|||
|
from the process and not returned until the data queue is uninitialized.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DataQueue - Supplies the data queue to be initialized.
|
|||
|
|
|||
|
Process - Supplies a pointer to the process creating the mailslot.
|
|||
|
|
|||
|
Quota - Supplies the quota to assign to the data queue.
|
|||
|
|
|||
|
MaximumMessageSize - The size of the largest message that can be
|
|||
|
written to the data queue.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
DebugTrace(+1, Dbg, "MsInitializeDataQueue, DataQueue = %08lx\n", (ULONG)DataQueue);
|
|||
|
|
|||
|
//
|
|||
|
// Get the process's quota, if we can't get it then this call will
|
|||
|
// raise status.
|
|||
|
//
|
|||
|
|
|||
|
Status = PsChargeProcessPagedPoolQuota (Process, Quota);
|
|||
|
|
|||
|
if (!NT_SUCCESS (Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
ObReferenceObject( Process );
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the data queue structure.
|
|||
|
//
|
|||
|
|
|||
|
DataQueue->BytesInQueue = 0;
|
|||
|
DataQueue->EntriesInQueue = 0;
|
|||
|
DataQueue->QueueState = Empty;
|
|||
|
DataQueue->MaximumMessageSize = MaximumMessageSize;
|
|||
|
DataQueue->Quota = Quota;
|
|||
|
DataQueue->QuotaUsed = 0;
|
|||
|
InitializeListHead( &DataQueue->DataEntryList );
|
|||
|
|
|||
|
//
|
|||
|
// Return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "MsInitializeDataQueue -> VOID\n", 0);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsUninitializeDataQueue (
|
|||
|
IN PDATA_QUEUE DataQueue,
|
|||
|
IN PEPROCESS Process
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine uninitializes a data queue. The previously debited quota
|
|||
|
is returned to the process.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DataQueue - Supplies the data queue being uninitialized
|
|||
|
|
|||
|
Process - Supplies a pointer to the process who created the mailslot
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
DebugTrace(+1, Dbg, "MsUninitializeDataQueue, DataQueue = %08lx\n", (ULONG)DataQueue);
|
|||
|
|
|||
|
//
|
|||
|
// Assert that the queue is empty
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( IsListEmpty(&DataQueue->DataEntryList) );
|
|||
|
ASSERT( DataQueue->BytesInQueue == 0);
|
|||
|
ASSERT( DataQueue->EntriesInQueue == 0);
|
|||
|
ASSERT( DataQueue->QuotaUsed == 0);
|
|||
|
|
|||
|
//
|
|||
|
// Return all of our quota back to the process
|
|||
|
//
|
|||
|
|
|||
|
PsReturnProcessPagedPoolQuota (Process, DataQueue->Quota);
|
|||
|
ObDereferenceObject (Process);
|
|||
|
|
|||
|
//
|
|||
|
// For safety's sake, zero out the data queue structure.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory (DataQueue, sizeof (DATA_QUEUE));
|
|||
|
|
|||
|
//
|
|||
|
// Return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "MsUnininializeDataQueue -> VOID\n", 0);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MsAddDataQueueEntry (
|
|||
|
IN PDATA_QUEUE DataQueue,
|
|||
|
IN QUEUE_STATE Who,
|
|||
|
IN ULONG DataSize,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PWORK_CONTEXT WorkContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function adds a new data entry to the of the data queue.
|
|||
|
Entries are always appended to the queue. If necessary this
|
|||
|
function will allocate a data entry buffer, or use space in
|
|||
|
the IRP.
|
|||
|
|
|||
|
The different actions we are perform are based on the type and who
|
|||
|
parameters and quota requirements.
|
|||
|
|
|||
|
|
|||
|
Who == ReadEntries
|
|||
|
|
|||
|
+----------+ - Allocate Data Entry from IRP
|
|||
|
|Irp | +-----------+
|
|||
|
|BufferedIo|<----|Buffered | - Allocate New System buffer
|
|||
|
|DeallBu...| |EitherQuota|
|
|||
|
+----------+ +-----------+ - Reference and modify Irp to
|
|||
|
| | | do Buffered I/O, Deallocate
|
|||
|
v | v buffer, and have I/O completion
|
|||
|
+------+ +------>+------+ copy the buffer (Input operation)
|
|||
|
|User | |System|
|
|||
|
|Buffer| |Buffer|
|
|||
|
+------+ +------+
|
|||
|
|
|||
|
Who == WriteEntries && Quota Available
|
|||
|
|
|||
|
+----------+ - Allocate Data Entry from Quota
|
|||
|
|Irp | +-----------+
|
|||
|
| | |Buffered | - Allocate New System buffer
|
|||
|
| | |Quota |
|
|||
|
+----------+ +-----------+ - Copy data from User buffer to
|
|||
|
| | system buffer
|
|||
|
v v
|
|||
|
+------+ +------+ - Complete IRP
|
|||
|
|User |..copy..>|System|
|
|||
|
|Buffer| |Buffer|
|
|||
|
+------+ +------+
|
|||
|
|
|||
|
Who == WriteEntries && Quota Not Available
|
|||
|
|
|||
|
+----------+ - Allocate Data Entry from Irp
|
|||
|
|Irp | +-----------+
|
|||
|
|BufferedIo|<----|Buffered | - Allocate New System buffer
|
|||
|
|DeallBuff | |UserQuota |
|
|||
|
+----------+ +-----------+ - Reference and modify Irp to use
|
|||
|
| | | the new system buffer, do Buffered
|
|||
|
v | v I/O, and Deallocate buffer
|
|||
|
+------+ +------>+------+
|
|||
|
|User | |System| - Copy data from User buffer to
|
|||
|
|Buffer|..copy..>|Buffer| system buffer
|
|||
|
+------+ +------+
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DataQueue - Supplies the Data queue being modified.
|
|||
|
|
|||
|
Who - Indicates if this is the reader or writer that is adding to the
|
|||
|
mailslot.
|
|||
|
|
|||
|
DataSize - Indicates the size of the data buffer needed to represent
|
|||
|
this entry.
|
|||
|
|
|||
|
Irp - Supplies a pointer to the IRP responsible for this entry.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PDATA_ENTRY - Returns a pointer to the newly added data entry.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDATA_ENTRY dataEntry;
|
|||
|
PLIST_ENTRY previousEntry;
|
|||
|
PFCB fcb;
|
|||
|
ULONG TotalSize;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "MsAddDataQueueEntry, DataQueue = %08lx\n", (ULONG)DataQueue);
|
|||
|
|
|||
|
ASSERT( DataQueue->QueueState != -1 );
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
if (Who == ReadEntries) {
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a data entry from the IRP, and allocate a new
|
|||
|
// system buffer.
|
|||
|
//
|
|||
|
|
|||
|
dataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp );
|
|||
|
|
|||
|
dataEntry->DataPointer = NULL;
|
|||
|
dataEntry->Irp = Irp;
|
|||
|
dataEntry->DataSize = DataSize;
|
|||
|
dataEntry->TimeoutWorkContext = WorkContext;
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if the mailslot has enough quota left to
|
|||
|
// allocate the system buffer.
|
|||
|
//
|
|||
|
|
|||
|
if ((DataQueue->Quota - DataQueue->QuotaUsed) >= DataSize) {
|
|||
|
|
|||
|
//
|
|||
|
// Use the mailslot quota to allocate pool for the request.
|
|||
|
//
|
|||
|
|
|||
|
if (DataSize) {
|
|||
|
dataEntry->DataPointer = MsAllocatePagedPoolCold( DataSize,
|
|||
|
'rFsM' );
|
|||
|
if (dataEntry->DataPointer == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
}
|
|||
|
DataQueue->QuotaUsed += DataSize;
|
|||
|
|
|||
|
dataEntry->From = MailslotQuota;
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Use the caller's quota to allocate pool for the request.
|
|||
|
//
|
|||
|
|
|||
|
if (DataSize) {
|
|||
|
dataEntry->DataPointer = MsAllocatePagedPoolWithQuotaCold( DataSize,
|
|||
|
'rFsM' );
|
|||
|
if (dataEntry->DataPointer == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dataEntry->From = UserQuota;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Modify the IRP to be buffered I/O, deallocate the buffer, copy
|
|||
|
// the buffer on completion, and to reference the new system
|
|||
|
// buffer.
|
|||
|
//
|
|||
|
|
|||
|
Irp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
|
|||
|
Irp->AssociatedIrp.SystemBuffer = dataEntry->DataPointer;
|
|||
|
if (Irp->AssociatedIrp.SystemBuffer) {
|
|||
|
Irp->Flags |= IRP_DEALLOCATE_BUFFER;
|
|||
|
}
|
|||
|
|
|||
|
Irp->IoStatus.Pointer = DataQueue;
|
|||
|
status = STATUS_PENDING;
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This is a writer entry.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// If there is enough quota left in the mailslot then we will
|
|||
|
// allocate the data entry and data buffer from the mailslot
|
|||
|
// quota.
|
|||
|
//
|
|||
|
TotalSize = sizeof(DATA_ENTRY) + DataSize;
|
|||
|
if (TotalSize < sizeof(DATA_ENTRY)) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
if ((DataQueue->Quota - DataQueue->QuotaUsed) >= TotalSize) {
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the data buffer using the mailslot quota.
|
|||
|
//
|
|||
|
|
|||
|
dataEntry = MsAllocatePagedPool( TotalSize, 'dFsM' );
|
|||
|
if (dataEntry == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
dataEntry->DataPointer = (PVOID) (dataEntry + 1);
|
|||
|
|
|||
|
DataQueue->QuotaUsed += TotalSize;
|
|||
|
|
|||
|
dataEntry->From = MailslotQuota;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// There isn't enough quota in the mailslot. Use the
|
|||
|
// caller's quota.
|
|||
|
//
|
|||
|
|
|||
|
dataEntry = MsAllocatePagedPoolWithQuota( TotalSize, 'dFsM' );
|
|||
|
if (dataEntry == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
dataEntry->DataPointer = (PVOID) (dataEntry + 1);
|
|||
|
|
|||
|
dataEntry->From = UserQuota;
|
|||
|
|
|||
|
}
|
|||
|
dataEntry->Irp = NULL;
|
|||
|
dataEntry->DataSize = DataSize;
|
|||
|
dataEntry->TimeoutWorkContext = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Copy the user buffer to the new system buffer, update the FCB
|
|||
|
// timestamps and complete the IRP.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
RtlCopyMemory (dataEntry->DataPointer, Irp->UserBuffer, DataSize);
|
|||
|
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
//
|
|||
|
// Only need to free the writers case as the readers get the buffer freed on I/O
|
|||
|
// completion.
|
|||
|
//
|
|||
|
if (Who == WriteEntries) {
|
|||
|
MsFreePool ( dataEntry );
|
|||
|
}
|
|||
|
|
|||
|
return GetExceptionCode (); // Watch out. Could be a guard page violation thats a warning!
|
|||
|
}
|
|||
|
|
|||
|
fcb = CONTAINING_RECORD( DataQueue, FCB, DataQueue );
|
|||
|
KeQuerySystemTime( &fcb->Specific.Fcb.LastModificationTime );
|
|||
|
|
|||
|
Irp->IoStatus.Information = DataSize;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} // else (writer entry)
|
|||
|
|
|||
|
//
|
|||
|
// Now data entry points to a new data entry to add to the data queue
|
|||
|
// Check if the queue is empty otherwise we will add this entry to
|
|||
|
// the end of the queue.
|
|||
|
//
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( IsListEmpty( &DataQueue->DataEntryList ) ) {
|
|||
|
|
|||
|
ASSERT( DataQueue->QueueState == Empty );
|
|||
|
ASSERT( DataQueue->BytesInQueue == 0);
|
|||
|
ASSERT( DataQueue->EntriesInQueue == 0);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ASSERT( DataQueue->QueueState == Who );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
DataQueue->QueueState = Who;
|
|||
|
|
|||
|
//
|
|||
|
// Only cound written bytes and messages in the queue. This makes sense because we return
|
|||
|
// this value as the end of file position. GetMailslotInfo needs EntriesInQueue to
|
|||
|
// ignore reads.
|
|||
|
//
|
|||
|
if (Who == WriteEntries) {
|
|||
|
DataQueue->BytesInQueue += dataEntry->DataSize;
|
|||
|
DataQueue->EntriesInQueue += 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Insert the new entry at the appropriate place in the data queue.
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList( &DataQueue->DataEntryList, &dataEntry->ListEntry );
|
|||
|
|
|||
|
WorkContext = dataEntry->TimeoutWorkContext;
|
|||
|
if ( WorkContext) {
|
|||
|
KeSetTimer( &WorkContext->Timer,
|
|||
|
WorkContext->Fcb->Specific.Fcb.ReadTimeout,
|
|||
|
&WorkContext->Dpc );
|
|||
|
}
|
|||
|
|
|||
|
if (Who == ReadEntries) {
|
|||
|
MsSetCancelRoutine( Irp ); // this fakes a call to cancel if we are already canceled
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
DumpDataQueue( "After AddDataQueueEntry\n", DataQueue );
|
|||
|
DebugTrace(-1, Dbg, "MsAddDataQueueEntry -> %08lx\n", (ULONG)dataEntry);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PIRP
|
|||
|
MsRemoveDataQueueEntry (
|
|||
|
IN PDATA_QUEUE DataQueue,
|
|||
|
IN PDATA_ENTRY DataEntry
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine removes the specified data entry from the indicated
|
|||
|
data queue, and possibly returns the IRP associated with the entry if
|
|||
|
it wasn't already completed.
|
|||
|
|
|||
|
If the data entry we are removing indicates buffered I/O then we also
|
|||
|
need to deallocate the data buffer besides the data entry but only
|
|||
|
if the IRP is null. Note that the data entry might be stored in an IRP.
|
|||
|
If it is then we are going to return the IRP it is stored in.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DataEntry - Supplies a pointer to the data entry to remove.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PIRP - Possibly returns a pointer to an IRP.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
FROM from;
|
|||
|
PIRP irp;
|
|||
|
ULONG dataSize;
|
|||
|
PVOID dataPointer;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "MsRemoveDataQueueEntry, DataEntry = %08lx\n", (ULONG)DataEntry);
|
|||
|
DebugTrace( 0, Dbg, "DataQueue = %08lx\n", (ULONG)DataQueue);
|
|||
|
|
|||
|
//
|
|||
|
// Remove the data entry from the queue and update the count of
|
|||
|
// data entries in the queue.
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList( &DataEntry->ListEntry );
|
|||
|
//
|
|||
|
// If the queue is now empty then we need to fix the queue
|
|||
|
// state.
|
|||
|
//
|
|||
|
|
|||
|
if (IsListEmpty( &DataQueue->DataEntryList ) ) {
|
|||
|
DataQueue->QueueState = Empty;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Capture some of the fields from the data entry to make our
|
|||
|
// other references a little easier.
|
|||
|
//
|
|||
|
|
|||
|
from = DataEntry->From;
|
|||
|
dataSize = DataEntry->DataSize;
|
|||
|
|
|||
|
|
|||
|
if (from == MailslotQuota) {
|
|||
|
DataQueue->QuotaUsed -= dataSize;
|
|||
|
}
|
|||
|
//
|
|||
|
// Get the IRP for this block if there is one
|
|||
|
//
|
|||
|
|
|||
|
irp = DataEntry->Irp;
|
|||
|
if (irp) {
|
|||
|
//
|
|||
|
// Cancel the timer associated with this if there is one
|
|||
|
//
|
|||
|
MsCancelTimer (DataEntry);
|
|||
|
irp = MsResetCancelRoutine( irp );
|
|||
|
if ( irp == NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// cancel is active. Let it know that we already did partial cleanup.
|
|||
|
// It just has to complete the IRP.
|
|||
|
//
|
|||
|
DataEntry->ListEntry.Flink = NULL;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
DataQueue->BytesInQueue -= DataEntry->DataSize;
|
|||
|
|
|||
|
//
|
|||
|
// Free the data entry for a write request. This is part of the IRP for a read request.
|
|||
|
//
|
|||
|
ExFreePool( DataEntry );
|
|||
|
|
|||
|
if (from == MailslotQuota) {
|
|||
|
DataQueue->QuotaUsed -= sizeof(DATA_ENTRY);
|
|||
|
}
|
|||
|
|
|||
|
DataQueue->EntriesInQueue--;
|
|||
|
#if DBG
|
|||
|
if (DataQueue->EntriesInQueue == 0) {
|
|||
|
ASSERT (DataQueue->QueueState == Empty);
|
|||
|
ASSERT (DataQueue->BytesInQueue == 0);
|
|||
|
ASSERT (IsListEmpty( &DataQueue->DataEntryList ));
|
|||
|
ASSERT (DataQueue->QuotaUsed == 0);
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
DumpDataQueue( "After RemoveDataQueueEntry\n", DataQueue );
|
|||
|
DebugTrace(-1, Dbg, "MsRemoveDataQueueEntry -> %08lx\n", (ULONG)irp);
|
|||
|
|
|||
|
return irp;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsRemoveDataQueueIrp (
|
|||
|
IN PIRP Irp,
|
|||
|
IN PDATA_QUEUE DataQueue
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine removes an IRP from its data queue.
|
|||
|
|
|||
|
Requirements:
|
|||
|
|
|||
|
The FCB for this data queue MUST be exclusively locked.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp being removed.
|
|||
|
|
|||
|
DataQueue - A pointer to the data queue structure where we expect
|
|||
|
to find the IRP.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns whether or not we actually dequeued the IRP.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDATA_ENTRY dataEntry;
|
|||
|
PLIST_ENTRY listEntry, nextListEntry;
|
|||
|
PWORK_CONTEXT workContext;
|
|||
|
PKTIMER timer;
|
|||
|
BOOLEAN foundIrp = FALSE;
|
|||
|
|
|||
|
dataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp );
|
|||
|
|
|||
|
//
|
|||
|
// This is the cancel path. If a completion path has already removed this IRP then return now.
|
|||
|
// The timer will have been canceled, counts adjusted etc.
|
|||
|
//
|
|||
|
if (dataEntry->ListEntry.Flink == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
//
|
|||
|
// remove this entry from the list.
|
|||
|
//
|
|||
|
RemoveEntryList (&dataEntry->ListEntry);
|
|||
|
|
|||
|
MsCancelTimer (dataEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the queue is now empty then we need to fix the queue
|
|||
|
// state.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Check if we need to return mailslot quota. The DATA_ENTRY was part of the IRP so we didn't
|
|||
|
// get charged for that
|
|||
|
//
|
|||
|
|
|||
|
if ( dataEntry->From == MailslotQuota ) {
|
|||
|
DataQueue->QuotaUsed -= dataEntry->DataSize;
|
|||
|
}
|
|||
|
|
|||
|
if (IsListEmpty( &DataQueue->DataEntryList ) ) {
|
|||
|
|
|||
|
DataQueue->QueueState = Empty;
|
|||
|
|
|||
|
ASSERT (DataQueue->BytesInQueue == 0);
|
|||
|
ASSERT (DataQueue->QuotaUsed == 0);
|
|||
|
ASSERT (DataQueue->EntriesInQueue == 0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // MsRemoveDataQueueIrp
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsCancelDataQueueIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the cancel function for an IRP saved in a
|
|||
|
data queue
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Device object associated with IRP or NULL if called directly by this driver
|
|||
|
|
|||
|
Irp - Supplies the Irp being cancelled.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFCB fcb;
|
|||
|
PDATA_QUEUE dataQueue;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// This isn't strictly correct. IoCancelIrp can be called at Irql <= DISPATCH_LEVEL but
|
|||
|
// this code is assuming that the IRQL of the caller is <= APC_LEVEL.
|
|||
|
// If we are called inline we don't hold the cancel spinlock and we already own the FCB lock.
|
|||
|
//
|
|||
|
if (DeviceObject != NULL) {
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
fileObject = irpSp->FileObject;
|
|||
|
|
|||
|
dataQueue = (PDATA_QUEUE)Irp->IoStatus.Pointer;
|
|||
|
|
|||
|
|
|||
|
fcb = CONTAINING_RECORD( dataQueue, FCB, DataQueue );
|
|||
|
|
|||
|
//
|
|||
|
// Get exclusive access to the mailslot FCB so we can now do our work.
|
|||
|
//
|
|||
|
if (DeviceObject != NULL) {
|
|||
|
FsRtlEnterFileSystem ();
|
|||
|
MsAcquireExclusiveFcb( fcb );
|
|||
|
}
|
|||
|
|
|||
|
MsRemoveDataQueueIrp( Irp, dataQueue );
|
|||
|
|
|||
|
if (DeviceObject != NULL) {
|
|||
|
MsReleaseFcb( fcb );
|
|||
|
FsRtlExitFileSystem ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
MsCompleteRequest( Irp, STATUS_CANCELLED );
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // MsCancelDataQueueIrp
|
|||
|
|
|||
|
PIRP
|
|||
|
MsResetCancelRoutine(
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Stub to null out the cancel routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp whose cancel routine is to be nulled out.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if ( IoSetCancelRoutine( Irp, NULL ) != NULL ) {
|
|||
|
return Irp;
|
|||
|
} else {
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
} // MsResetCancelRoutine
|
|||
|
|
|||
|
VOID
|
|||
|
MsSetCancelRoutine(
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Stub to set the cancel routine. If the irp has already been cancelled,
|
|||
|
the cancel routine is called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp whose cancel routine is to be set.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
IoMarkIrpPending( Irp ); // top level always returns STATUS_PENDING if we get this far
|
|||
|
|
|||
|
IoSetCancelRoutine( Irp, MsCancelDataQueueIrp );
|
|||
|
if ( Irp->Cancel && IoSetCancelRoutine( Irp, NULL ) != NULL ) {
|
|||
|
//
|
|||
|
// The IRP was canceled before we put our routine on. Fake a cancel call
|
|||
|
//
|
|||
|
|
|||
|
MsCancelDataQueueIrp (NULL, Irp);
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // MsSetCancelRoutine
|
|||
|
|