📄 cancelsafe.c
字号:
// Get a pointer to the instance context.
//
InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );
//
// If the supplied callback "Data" is NULL, the "NextIo" is the first entry
// in the queue; or it is the next list entry in the queue.
//
if (Data == NULL) {
NextEntry = InstCtx->QueueHead.Flink;
} else {
NextEntry = Data->QueueLinks.Flink;
}
//
// Return NULL if we hit the end of the queue or the queue is empty.
//
if (NextEntry == &InstCtx->QueueHead) {
return NULL;
}
NextData = CONTAINING_RECORD( NextEntry, FLT_CALLBACK_DATA, QueueLinks );
return NextData;
}
VOID
CsqCompleteCanceledIo(
__in PFLT_CALLBACK_DATA_QUEUE DataQueue,
__inout PFLT_CALLBACK_DATA Data
)
/*++
Routine Description:
FltMgr calls this routine to complete an operation as cancelled that was
previously pended. The queue is already locked before this routine is called.
Arguments:
DataQueue - Supplies a pointer to the queue itself.
Data - Supplies the callback data that is to be canceled.
Return Value:
None.
--*/
{
PQUEUE_CONTEXT QueueCtx;
UNREFERENCED_PARAMETER( DataQueue );
DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
("[Csq]: CancelSafe!CsqCompleteCanceledIo\n") );
QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];
//
// Just complete the operation as canceled.
//
Data->IoStatus.Status = STATUS_CANCELLED;
Data->IoStatus.Information = 0;
FltCompletePendedPreOperation( Data,
FLT_PREOP_COMPLETE,
0 );
//
// Free the extra storage that was allocated for this canceled I/O.
//
ExFreeToNPagedLookasideList( &Globals.OueueContextLookaside,
QueueCtx );
}
FLT_PREOP_CALLBACK_STATUS
PreRead (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID *CompletionContext
)
/*++
Routine Description:
Handle pre-read.
Arguments:
Data - Pointer to the filter callbackData that is passed to us.
FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
opaque handles to this filter, instance, its associated volume and
file object.
CompletionContext - The context for the completion routine for this
operation.
Return Value:
The return value is the status of the operation.
--*/
{
PINSTANCE_CONTEXT InstCtx = NULL;
PQUEUE_CONTEXT QueueCtx = NULL;
PFLT_FILE_NAME_INFORMATION NameInfo = NULL;
PFLT_GENERIC_WORKITEM WorkItem = NULL;
NTSTATUS CbStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS Status;
DebugTrace( CSQ_TRACE_PRE_READ,
("[Csq]: CancelSafe!PreRead\n") );
//
// Skip IRP_PAGING_IO, IRP_SYNCHRONOUS_PAGING_IO and
// TopLevelIrp.
//
if ((Data->Iopb->IrpFlags & IRP_PAGING_IO) ||
(Data->Iopb->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ||
IoGetTopLevelIrp()) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
//
// Get and parse the file name
//
Status = FltGetFileNameInformation( Data,
FLT_FILE_NAME_NORMALIZED
| FLT_FILE_NAME_QUERY_DEFAULT,
&NameInfo );
if (!NT_SUCCESS( Status )) {
DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
("[Csq]: Failed to get filename (Status = 0x%x)\n",
Status) );
goto PreReadCleanup;
}
Status = FltParseFileNameInformation( NameInfo );
if (!NT_SUCCESS( Status )) {
DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
("[Csq]: Failed to parse filename (Name = %wZ, Status = 0x%x)\n",
&NameInfo->Name,
Status) );
goto PreReadCleanup;
}
//
// Compare to see if this is the magic file whose I/O is to be pended.
//
if (RtlCompareUnicodeString( &NameInfo->FinalComponent,
&CsqFile,
TRUE ) != 0) {
goto PreReadCleanup;
}
//
// Since Fast I/O operations cannot be queued, we could return
// FLT_PREOP_SUCCESS_NO_CALLBACK at this point. In this sample,
// we disallow Fast I/O for this magic file in order to force an IRP
// to be sent to us again. The purpose of doing that is to demonstrate
// the cancel safe queue, which may not be true in the real world.
//
if (!FLT_IS_IRP_OPERATION( Data )) {
CbStatus = FLT_PREOP_DISALLOW_FASTIO;
goto PreReadCleanup;
}
//
// Allocate a context for each I/O to be inserted into the queue.
//
QueueCtx = ExAllocateFromNPagedLookasideList( &Globals.OueueContextLookaside );
if (QueueCtx == NULL) {
DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
("[Csq]: Failed to allocate from NPagedLookasideList (Status = 0x%x)\n",
Status) );
goto PreReadCleanup;
}
RtlZeroMemory(QueueCtx, sizeof(QUEUE_CONTEXT));
//
// Get the instance context.
//
Status = FltGetInstanceContext( FltObjects->Instance,
&InstCtx );
if (!NT_SUCCESS( Status )) {
ASSERT( !"Instance context is missing" );
goto PreReadCleanup;
}
//
// Set the queue context
//
Data->QueueContext[0] = (PVOID) QueueCtx;
Data->QueueContext[1] = NULL;
//
// Insert the callback data into the cancel safe queue
//
Status = FltCbdqInsertIo( &InstCtx->Cbdq,
Data,
&QueueCtx->CbdqIoContext,
0 );
if (Status == STATUS_SUCCESS) {
//
// In general, we can create a worker thread here as long as we can
// correctly handle the insert/remove race conditions b/w multi threads.
// In this sample, the worker thread creation is done in CsqInsertIo.
// This is a simpler solution because CsqInsertIo is atomic with
// respect to other CsqXxxIo callback routines.
//
CbStatus = FLT_PREOP_PENDING;
} else {
DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
("[Csq]: Failed to insert into cbdq (Status = 0x%x)\n",
Status) );
}
PreReadCleanup:
//
// Clean up
//
if (QueueCtx && CbStatus != FLT_PREOP_PENDING) {
ExFreeToNPagedLookasideList( &Globals.OueueContextLookaside, QueueCtx );
}
if (NameInfo) {
FltReleaseFileNameInformation( NameInfo );
}
if (InstCtx) {
FltReleaseContext( InstCtx );
}
return CbStatus;
}
VOID
PreReadWorkItemRoutine(
__in PFLT_GENERIC_WORKITEM WorkItem,
__in PFLT_FILTER Filter,
__in PVOID Context
)
/*++
Routine Description:
This WorkItem routine is called in the system thread context to process
all the pended I/O in this mini filter's cancel safe queue. For each I/O
in the queue, it completes the I/O after pending the operation for a
period of time. The thread exits when the queue is empty.
Arguments:
WorkItem - Unused.
Filter - Unused.
Context - Context information.
Return Value:
None.
--*/
{
PINSTANCE_CONTEXT InstCtx = NULL;
PFLT_CALLBACK_DATA Data;
PFLT_INSTANCE Instance = (PFLT_INSTANCE)Context;
PQUEUE_CONTEXT QueueCtx;
NTSTATUS Status;
UNREFERENCED_PARAMETER( WorkItem );
UNREFERENCED_PARAMETER( Filter );
DebugTrace( CSQ_TRACE_PRE_READ,
("[Csq]: CancelSafe!PreReadWorkItemRoutine\n") );
//
// Get a pointer to the instance context.
//
Status = FltGetInstanceContext( Instance,
&InstCtx );
if (!NT_SUCCESS( Status ))
{
ASSERT( !"Instance Context is missing" );
return;
}
//
// Process all the pended I/O in the cancel safe queue
//
for (;;) {
PreReadPendIo( InstCtx );
//
// WorkerThreadFlag >= 1;
// Here we reduce it to 1.
//
InterlockedExchange( &InstCtx->WorkerThreadFlag, 1 );
//
// Remove an I/O from the cancel safe queue.
//
Data = FltCbdqRemoveNextIo( &InstCtx->Cbdq,
NULL);
if (Data) {
QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];
PreReadProcessIo( Data );
//
// Lock the user buffer before sending the I/O down,
// because we are running in a system thread context.
//
if ( Data->Iopb->Parameters.Read.MdlAddress == NULL &&
!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) ) {
Status = FltLockUserBuffer( Data );
if (!NT_SUCCESS(Status)) {
DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
("[Csq]: Failed to lock user buffer (Status = 0x%x)\n",
Status) );
}
}
//
// Complete the I/O
//
FltCompletePendedPreOperation( Data,
FLT_PREOP_SUCCESS_NO_CALLBACK,
NULL );
//
// Free the extra storage that was allocated for this I/O.
//
ExFreeToNPagedLookasideList( &Globals.OueueContextLookaside,
QueueCtx );
} else {
//
// At this moment it is possible that a new IO is being inserted
// into the queue in the CsqInsertIo routine. Now that the queue is
// empty, CsqInsertIo needs to make a decision on whether to create
// a new worker thread. The decision is based on the race between
// the InterlockedIncrement in CsqInsertIo and the
// InterlockedDecrement as below. There are two situations:
//
// (1) If the decrement executes earlier before the increment,
// the flag will be decremented to 0 so this worker thread
// will return. Then CsqInsertIo will increment the flag
// from 0 to 1, and therefore create a new worker thread.
// (2) If the increment executes earlier before the decrement,
// the flag will be first incremented to 2 in CsqInsertIo
// so a new worker thread will not be satisfied. Then the
// decrement as below will lower the flag down to 1, and
// therefore continue this worker thread.
//
if (InterlockedDecrement( &InstCtx->WorkerThreadFlag ) == 0)
break;
}
}
//
// Clean up
//
FltReleaseContext(InstCtx);
FltFreeGenericWorkItem(WorkItem);
}
NTSTATUS
PreReadPendIo(
__in PINSTANCE_CONTEXT InstanceContext
)
/*++
Routine Description:
This routine waits for a period of time or until the instance is
torndown.
Arguments:
InstanceContext - Supplies a pointer to the instance context.
Return Value:
The return value is the status of the operation.
--*/
{
LARGE_INTEGER DueTime;
NTSTATUS Status;
//
// Delay 15 seconds or get signaled if the instance is torndown.
//
DueTime.QuadPart = (LONGLONG) - (15 * 1000 * 1000 * 10);
Status = KeWaitForSingleObject( &InstanceContext->TeardownEvent,
Executive,
KernelMode,
FALSE,
&DueTime );
return Status;
}
NTSTATUS
PreReadProcessIo(
__inout PFLT_CALLBACK_DATA Data
)
/*++
Routine Description:
This routine process the I/O that was removed from the queue.
Arguments:
Data - Supplies the callback data that was removed from the queue.
Return Value:
The return value is the status of the operation.
--*/
{
UNREFERENCED_PARAMETER( Data );
return STATUS_SUCCESS;
}
VOID
PreReadEmptyQueueAndComplete(
__in PINSTANCE_CONTEXT InstanceContext
)
/*++
Routine Description:
This routine empties the cancel safe queue and complete all the
pended pre-read operations.
Arguments:
InstanceContext - Supplies a pointer to the instance context.
Return Value:
None.
--*/
{
PFLT_CALLBACK_DATA Data;
PQUEUE_CONTEXT QueueCtx;
do {
Data = FltCbdqRemoveNextIo( &InstanceContext->Cbdq,
NULL );
if (Data) {
QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];
if ( Data->Iopb->Parameters.Read.MdlAddress == NULL &&
!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) ) {
(VOID) FltLockUserBuffer( Data );
}
FltCompletePendedPreOperation( Data,
FLT_PREOP_SUCCESS_NO_CALLBACK,
NULL );
ExFreeToNPagedLookasideList( &Globals.OueueContextLookaside,
QueueCtx );
}
} while (Data);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -