859 lines
20 KiB
C
859 lines
20 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
|
||
|