⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cancelsafe.c

📁 这是一款驱动过滤程序 能轻松过滤文本文档 幻灯片等程序
💻 C
📖 第 1 页 / 共 3 页
字号:
    //  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 + -