885 lines
22 KiB
C
885 lines
22 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
|
|||
|
MsCancelDataQueueIrp (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
MsResetCancelRoutine(
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
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 )
|
|||
|
#endif
|
|||
|
|
|||
|
#if 0
|
|||
|
NOT PAGEABLE -- MsCancelDataQueueIrp
|
|||
|
NOT PAGEABLE -- MsResetCancelRoutine
|
|||
|
NOT PAGEABLE -- MsSetCancelRoutine
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
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.
|
|||
|
//
|
|||
|
|
|||
|
ObReferenceObject( Process );
|
|||
|
PsChargePoolQuota( Process, PagedPool, Quota );
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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) );
|
|||
|
|
|||
|
//
|
|||
|
// Return all of our quota back to the process
|
|||
|
//
|
|||
|
|
|||
|
PsReturnPoolQuota( Process, PagedPool, 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;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDATA_ENTRY
|
|||
|
MsAddDataQueueEntry (
|
|||
|
IN PDATA_QUEUE DataQueue,
|
|||
|
IN QUEUE_STATE Who,
|
|||
|
IN ULONG DataSize,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
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, and possibly complete the indicated IRP.
|
|||
|
|
|||
|
The different actions we are perform are based on the type and who
|
|||
|
parameters and quota requirements.
|
|||
|
|
|||
|
*** This function must be called from within a try statement.
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
PVOID rewind1 = NULL;
|
|||
|
PVOID rewind2 = NULL;
|
|||
|
BOOLEAN irpCompleted = FALSE;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "MsAddDataQueueEntry, DataQueue = %08lx\n", (ULONG)DataQueue);
|
|||
|
|
|||
|
ASSERT( DataQueue->QueueState != -1 );
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
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 = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
rewind1 = dataEntry->DataPointer =
|
|||
|
FsRtlAllocatePool(
|
|||
|
PagedPool,
|
|||
|
DataSize == 0 ? 1 : DataSize
|
|||
|
);
|
|||
|
|
|||
|
DataQueue->QuotaUsed += DataSize;
|
|||
|
|
|||
|
dataEntry->From = MailslotQuota;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Use the caller's quota to allocate pool for the request.
|
|||
|
//
|
|||
|
|
|||
|
rewind1 = dataEntry->DataPointer =
|
|||
|
FsRtlAllocatePoolWithQuota(
|
|||
|
PagedPool,
|
|||
|
DataSize == 0 ? 1 : DataSize
|
|||
|
);
|
|||
|
|
|||
|
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_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
|
|||
|
Irp->AssociatedIrp.SystemBuffer = dataEntry->DataPointer;
|
|||
|
|
|||
|
//
|
|||
|
// Setup to add this entry to the end of the data queue.
|
|||
|
//
|
|||
|
|
|||
|
previousEntry = &DataQueue->DataEntryList;
|
|||
|
Irp->IoStatus.Status = (ULONG)DataQueue;
|
|||
|
|
|||
|
} 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.
|
|||
|
//
|
|||
|
|
|||
|
if ((DataQueue->Quota - DataQueue->QuotaUsed) >= sizeof(DATA_ENTRY) + DataSize) {
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the data buffer using the mailslot quota.
|
|||
|
//
|
|||
|
|
|||
|
rewind2 = dataEntry =
|
|||
|
FsRtlAllocatePool(
|
|||
|
PagedPool,
|
|||
|
sizeof(DATA_ENTRY)
|
|||
|
);
|
|||
|
|
|||
|
rewind1 = dataEntry->DataPointer =
|
|||
|
FsRtlAllocatePool(
|
|||
|
PagedPool,
|
|||
|
DataSize == 0 ? 1 : DataSize
|
|||
|
);
|
|||
|
|
|||
|
DataQueue->QuotaUsed += sizeof(DATA_ENTRY) + DataSize;
|
|||
|
|
|||
|
dataEntry->From = MailslotQuota;
|
|||
|
dataEntry->Irp = NULL;
|
|||
|
dataEntry->DataSize = DataSize;
|
|||
|
dataEntry->TimeoutWorkContext = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// There isn't enough quota in the mailslot. Use the
|
|||
|
// caller's quota.
|
|||
|
//
|
|||
|
// Allocate a data entry from the Irp, and allocate a new
|
|||
|
// system buffer.
|
|||
|
//
|
|||
|
|
|||
|
rewind2 = dataEntry =
|
|||
|
(PDATA_ENTRY)FsRtlAllocatePoolWithQuota(
|
|||
|
PagedPool,
|
|||
|
sizeof( DATA_ENTRY )
|
|||
|
);
|
|||
|
|
|||
|
dataEntry->From = UserQuota;
|
|||
|
dataEntry->Irp = NULL;
|
|||
|
dataEntry->DataSize = DataSize;
|
|||
|
dataEntry->TimeoutWorkContext = NULL;
|
|||
|
|
|||
|
rewind1 = dataEntry->DataPointer =
|
|||
|
FsRtlAllocatePoolWithQuota(
|
|||
|
PagedPool,
|
|||
|
DataSize == 0 ? 1 : DataSize
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the user buffer to the new system buffer, update the FCB
|
|||
|
// timestamps and complete the IRP.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
RtlMoveMemory( dataEntry->DataPointer, Irp->UserBuffer, DataSize );
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
|
|||
|
}
|
|||
|
|
|||
|
fcb = CONTAINING_RECORD( DataQueue, FCB, DataQueue );
|
|||
|
KeQuerySystemTime( &fcb->Specific.Fcb.LastModificationTime );
|
|||
|
|
|||
|
MsCompleteRequest( Irp, STATUS_SUCCESS );
|
|||
|
irpCompleted = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Find the place in the queue to insert this data entry.
|
|||
|
//
|
|||
|
|
|||
|
previousEntry = &DataQueue->DataEntryList;
|
|||
|
|
|||
|
} // 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 ( IsListEmpty( &DataQueue->DataEntryList ) ) {
|
|||
|
|
|||
|
ASSERT( DataQueue->QueueState == Empty );
|
|||
|
DataQueue->QueueState = Who;
|
|||
|
DataQueue->BytesInQueue = dataEntry->DataSize;
|
|||
|
DataQueue->EntriesInQueue = 1;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ASSERT( DataQueue->QueueState == Who );
|
|||
|
DataQueue->BytesInQueue += dataEntry->DataSize;
|
|||
|
DataQueue->EntriesInQueue += 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Insert the new entry at the appropriate place in the data queue.
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList( previousEntry, &dataEntry->ListEntry );
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
if ( AbnormalTermination() ) {
|
|||
|
if ( rewind1 != NULL ) {
|
|||
|
ExFreePool( rewind1 );
|
|||
|
}
|
|||
|
if ( rewind2 != NULL ) {
|
|||
|
ExFreePool( rewind2 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we have not already completed the IRP, see if the IRP has
|
|||
|
// been cancelled.
|
|||
|
//
|
|||
|
|
|||
|
if ( !irpCompleted ) {
|
|||
|
|
|||
|
MsSetCancelRoutine( Irp );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
DumpDataQueue( "After AddDataQueueEntry\n", DataQueue );
|
|||
|
DebugTrace(-1, Dbg, "MsAddDataQueueEntry -> %08lx\n", (ULONG)dataEntry);
|
|||
|
|
|||
|
return dataEntry;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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.
|
|||
|
//
|
|||
|
|
|||
|
--DataQueue->EntriesInQueue;
|
|||
|
RemoveEntryList( &DataEntry->ListEntry );
|
|||
|
|
|||
|
//
|
|||
|
// If the queue is now empty, update the state.
|
|||
|
//
|
|||
|
|
|||
|
if (DataQueue->EntriesInQueue == 0) {
|
|||
|
DataQueue->QueueState = Empty;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Capture some of the fields from the data entry to make our
|
|||
|
// other references a little easier.
|
|||
|
//
|
|||
|
|
|||
|
from = DataEntry->From;
|
|||
|
irp = DataEntry->Irp;
|
|||
|
dataSize = DataEntry->DataSize;
|
|||
|
dataPointer = DataEntry->DataPointer;
|
|||
|
|
|||
|
//
|
|||
|
// Check if we need to deallocate the data buffer, we only need
|
|||
|
// to deallocate if the IRP is null.
|
|||
|
//
|
|||
|
|
|||
|
if ( irp == NULL ) {
|
|||
|
ExFreePool( dataPointer );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The preceding call returned the user's quota or it
|
|||
|
// simply deallocated the buffer. If it only deallocated
|
|||
|
// the buffer then we need to credit our quota.
|
|||
|
//
|
|||
|
|
|||
|
if (from == MailslotQuota) {
|
|||
|
DataQueue->QuotaUsed -= dataSize;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if we still have an IRP to return. If we do then
|
|||
|
// we know that this data entry is located in the current IRP
|
|||
|
// stack location and we need to zero out the data entry (skipping
|
|||
|
// over the spare field), otherwise we got allocated from either
|
|||
|
// the mailslot or user quota, and we need to deallocate it ourselves.
|
|||
|
//
|
|||
|
|
|||
|
if (irp != NULL) {
|
|||
|
|
|||
|
DataEntry->From = 0;
|
|||
|
DataEntry->Irp = 0;
|
|||
|
DataEntry->DataSize = 0;
|
|||
|
DataEntry->DataPointer = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Null out the cancel routine
|
|||
|
//
|
|||
|
|
|||
|
MsResetCancelRoutine( irp );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ExFreePool( DataEntry );
|
|||
|
|
|||
|
//
|
|||
|
// The preceding call returned the user's quota or it
|
|||
|
// simply deallocated the buffer. If it only deallocated
|
|||
|
// the buffer then we need to credit the mailslot quota.
|
|||
|
//
|
|||
|
|
|||
|
if (from == MailslotQuota) {
|
|||
|
DataQueue->QuotaUsed -= sizeof(DATA_ENTRY);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
DumpDataQueue( "After RemoveDataQueueEntry\n", DataQueue );
|
|||
|
DebugTrace(-1, Dbg, "MsRemoveDataQueueEntry -> %08lx\n", (ULONG)irp);
|
|||
|
|
|||
|
return irp;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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 - ignored
|
|||
|
|
|||
|
Irp - Supplies the Irp being cancelled. A pointer to the data queue
|
|||
|
structure is stored in the information field of the Irp Iosb
|
|||
|
field.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFCB fcb;
|
|||
|
PDATA_QUEUE dataQueue;
|
|||
|
|
|||
|
PDATA_ENTRY dataEntry;
|
|||
|
PLIST_ENTRY listEntry, nextListEntry;
|
|||
|
|
|||
|
PWORK_CONTEXT workContext;
|
|||
|
PKTIMER timer;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|||
|
|
|||
|
//
|
|||
|
// The status field is used to store a pointer to the data queue
|
|||
|
// containing this IRP.
|
|||
|
//
|
|||
|
|
|||
|
dataQueue = (PDATA_QUEUE)Irp->IoStatus.Status;
|
|||
|
|
|||
|
//
|
|||
|
// We now need to void the cancel routine and release the io cancel
|
|||
|
// spin-lock.
|
|||
|
//
|
|||
|
|
|||
|
IoSetCancelRoutine( Irp, NULL );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Get exclusive access to the mailslot FCB so we can now do our work.
|
|||
|
//
|
|||
|
|
|||
|
fcb = CONTAINING_RECORD( dataQueue, FCB, DataQueue );
|
|||
|
MsAcquireExclusiveFcb( fcb );
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Scan the data queue looking for entries that have IRPs that
|
|||
|
// have been cancelled.
|
|||
|
//
|
|||
|
|
|||
|
listEntry = dataQueue->DataEntryList.Flink;
|
|||
|
|
|||
|
while ( listEntry != &dataQueue->DataEntryList ) {
|
|||
|
|
|||
|
nextListEntry = listEntry->Flink;
|
|||
|
|
|||
|
dataEntry = (PDATA_ENTRY)CONTAINING_RECORD(
|
|||
|
listEntry,
|
|||
|
DATA_ENTRY,
|
|||
|
ListEntry);
|
|||
|
//
|
|||
|
// If the data entry contains an Irp and the irp is cancelled then
|
|||
|
// we have some work to do.
|
|||
|
//
|
|||
|
|
|||
|
if ( (dataEntry->Irp != NULL) && (dataEntry->Irp->Cancel)) {
|
|||
|
|
|||
|
//
|
|||
|
// Cancel the timer, if there is one for this IRP.
|
|||
|
//
|
|||
|
|
|||
|
workContext = dataEntry->TimeoutWorkContext;
|
|||
|
if (workContext != NULL) {
|
|||
|
|
|||
|
DebugTrace( 0, Dbg, "Cancelling a timer\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// There was a timer on this read operation. Attempt
|
|||
|
// to cancel the operation. If the cancel operation
|
|||
|
// is successful, then we must cleanup after the operation.
|
|||
|
// If it was unsuccessful the timer DPC will run, and
|
|||
|
// will eventually cleanup.
|
|||
|
//
|
|||
|
|
|||
|
timer = &workContext->Timer;
|
|||
|
|
|||
|
if (KeCancelTimer( timer ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Release the reference to the FCB.
|
|||
|
//
|
|||
|
|
|||
|
MsDereferenceFcb( workContext->Fcb );
|
|||
|
|
|||
|
//
|
|||
|
// Free the memory from the work context, the timer,
|
|||
|
// and the DPC.
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool( workContext );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remove this data entry from the queue.
|
|||
|
//
|
|||
|
|
|||
|
RemoveEntryList( listEntry );
|
|||
|
--dataQueue->EntriesInQueue;
|
|||
|
|
|||
|
//
|
|||
|
// If the queue is now empty then we need to fix the queue
|
|||
|
// state.
|
|||
|
//
|
|||
|
|
|||
|
if (IsListEmpty( &dataQueue->DataEntryList ) ) {
|
|||
|
dataQueue->QueueState = Empty;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if we need to return mailslot quota.
|
|||
|
//
|
|||
|
|
|||
|
if ( dataEntry->From == MailslotQuota ) {
|
|||
|
dataQueue->QuotaUsed -= dataEntry->DataSize;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Complete the request saying that it has been cancelled.
|
|||
|
//
|
|||
|
|
|||
|
MsCompleteRequest( dataEntry->Irp, STATUS_CANCELLED );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
listEntry = nextListEntry;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
MsReleaseFcb( fcb );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // MsCancelDataQueueIrp
|
|||
|
|
|||
|
VOID
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
|||
|
IoSetCancelRoutine( Irp, NULL );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // 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.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
|||
|
|
|||
|
if ( Irp->Cancel ) {
|
|||
|
|
|||
|
//
|
|||
|
// The cancel spinlock will be released by this call.
|
|||
|
//
|
|||
|
|
|||
|
MsCancelDataQueueIrp( NULL, Irp );
|
|||
|
|
|||
|
} else {
|
|||
|
IoSetCancelRoutine( Irp, MsCancelDataQueueIrp );
|
|||
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // MsSetCancelRoutine
|