您的位置:首页 > Web前端

experiment : 基于wdk's cancelsafe 例程, 拒绝文件创建

2013-07-27 23:00 417 查看
WDK7600中提供了cancelSafe例程, 用于演示如何将挂起的IRP操作放入挂住队列.

但是例子中在挂住队列WorkItem中的操作只是超时, 超时后, 还是会让文件继续按照原来的请求继续操作.

我想做的是: 在基于MiniFilter的主防中, 遇到有r3请求的文件后缀文件被创建时, 将拦到的IRP操作插入挂住队列,返回挂住状态.

然后由r3决断, 是否对该文件创建操作放行还是拒绝.

网上找了很长时间, 没有找到基于minifilter + cancelsafe, 对文件操作进行干预的Demo。

网上的一般做法都是拦到文件操作时, 直接让r3裁决. 这样可能有效率问题, 没有充分利用minfilter提供的挂住机制 .

今天实验出来了.

发现在挂住队列的WorkItem中, 需要同时设置CALL_BACK_DATA 和 NTSTATUS, 然后调用 FltCompletePendedPreOperation

然后就看见效果(拒绝文件创建或允许文件创建).

我的工程基于WDK提供的miniflter那几个Demo, 根据需要整理了一下, 和原始Demo有点不像了~

下面是有关于 minifilter + cancelSafe 干预文件操作的代码片段,

DataQueue.h, DataQueue.c : cancelsafe操作中的挂住队列操作整理在这2个文件中.

/// @file       DataQueue.h
/// @brief      数据队列的定义

#ifndef __DATA_QUEUE_H__
#define __DATA_QUEUE_H__

#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>
#include "constDefine.h"

///  Queue context data structure
typedef struct _QUEUE_CONTEXT
{
    FLT_CALLBACK_DATA_QUEUE_IO_CONTEXT CbdqIoContext;

    /// 队列中的文件上报信息
    TAG_FILE_OPT_INFO   fileInfo;
} QUEUE_CONTEXT, *PQUEUE_CONTEXT;

/// Instance context data structure
typedef struct _INSTANCE_CONTEXT
{
    /// Instance for this context.
    PFLT_INSTANCE Instance;

    /// Cancel safe queue members
    FLT_CALLBACK_DATA_QUEUE Cbdq;

    LIST_ENTRY QueueHead;
    FAST_MUTEX Lock;

    /// Flag to control the life/death of the work item thread
    volatile LONG WorkerThreadFlag;

    /// Notify the worker thread that the instance is being torndown
    KEVENT TeardownEvent;
} INSTANCE_CONTEXT, *PINSTANCE_CONTEXT;

/*++

Routine Description:

    FltMgr calls this routine to acquire the lock protecting the queue.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Irql - Returns the previous IRQL if a spinlock is acquired.  We do not use
           any spinlocks, so we ignore this.

Return Value:

    None.

--*/
VOID
CbdqAcquire(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __out PKIRQL Irql);

/*++

Routine Description:

    FltMgr calls this routine to release the lock protecting the queue.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Irql - Supplies the previous IRQL if a spinlock is acquired.  We do not use
           any spinlocks, so we ignore this.

Return Value:

    None.

--*/
VOID
CbdqRelease(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in KIRQL Irql
    );

/*++

Routine Description:

    FltMgr calls this routine to insert an entry into our pending I/O queue.
    The queue is already locked before this routine is called.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Data - Supplies the callback data for the operation that is being
           inserted into the queue.

    Context - Supplies user-defined context information.

Return Value:

    STATUS_SUCCESS if the function completes successfully.  Otherwise a valid
    NTSTATUS code is returned.

--*/
NTSTATUS
CbdqInsertIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in PFLT_CALLBACK_DATA Data,
    __in_opt PVOID Context
    );

/*++

Routine Description:

    FltMgr calls this routine to remove an entry from our pending I/O queue.
    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 removed.

Return Value:

    None.

--*/
VOID
CbdqRemoveIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in PFLT_CALLBACK_DATA Data
    );

/*++

Routine Description:

    FltMgr calls this routine to look for an entry on our pending I/O queue.
    The queue is already locked before this routine is called.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Data - Supplies the callback data we should start our search from.
           If this is NULL, we start at the beginning of the list.

    PeekContext - Supplies user-defined context information.

Return Value:

    A pointer to the next callback data structure, or NULL.

--*/
PFLT_CALLBACK_DATA
CbdqPeekNextIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in_opt PFLT_CALLBACK_DATA Data,
    __in_opt PVOID PeekContext
    );

/*++

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.

--*/
VOID
CbdqCompleteCanceledIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __inout PFLT_CALLBACK_DATA Data
    );

/*++

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.

--*/
VOID
PreIrpMjCreateWorkItemRoutine(
    __in PFLT_GENERIC_WORKITEM WorkItem,
    __in PFLT_FILTER Filter,
    __in PVOID Context
    );

/*++

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.

--*/
NTSTATUS
PreIrpMjCreatePendIo(
    __in PINSTANCE_CONTEXT InstanceContext
    );

/// @fn     PreIrpMjCreateProcessIo
/// @brief  如果要拒绝, 必须要同时设置Data和callbackStatus, 如下组合
///         pData->IoStatus.Status = STATUS_ACCESS_DENIED;
///         *pcbStatus = FLT_PREOP_COMPLETE;
/// @param  PFLT_CALLBACK_DATA pData
///         Supplies the callback data that was removed from the queue.
/// @param  PINSTANCE_CONTEXT pInstCtx
///         上下文, 有事件 TeardownEvent 等待r3通知
/// @param  FLT_PREOP_CALLBACK_STATUS * pcbStatus
///         回调的状态
/// @return NTSTATUS
///         返回值用不到, 都设置在pData和cbStatus中
NTSTATUS
PreIrpMjCreateProcessIo(
    PFLT_CALLBACK_DATA pData,
    PINSTANCE_CONTEXT pInstCtx,
    FLT_PREOP_CALLBACK_STATUS * pcbStatus);

/*++

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.

--*/
VOID
PreIrpMjCreateEmptyQueueAndComplete(
    __in PINSTANCE_CONTEXT InstanceContext
    );

/// @fn     ConvertR3Answer
/// @brief  将r3回答的动作码, 转换成r0的IO状态码
NTSTATUS ConvertR3Answer(DWORD dwAnswerByR3);

#endif  // #ifndef __DATA_QUEUE_H__


/// @file       DataQueue.c
/// @brief      Cbdq callback routines

#include "DataQueue.h"
#include "DebugDefine.h"
#include "constDefine.h"
#include "timeWrapper.h"
#include "LockOpt.h"

extern const UNICODE_STRING CbdqFile;

VOID
CbdqAcquire(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __out PKIRQL Irql
    )
{
    PINSTANCE_CONTEXT InstCtx;

    UNREFERENCED_PARAMETER( Irql );

    DebugTrace( DBGLOG_CBDQ_CALLBACK,
                ("[%ws]: CbdqAcquire\n",
                DRIVER_NAME) );

    ///  Get a pointer to the instance context.
    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    ///  Acquire the lock.
    ExAcquireFastMutex( &InstCtx->Lock );
}

VOID
CbdqRelease(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in KIRQL Irql
    )
{
    PINSTANCE_CONTEXT InstCtx;

    UNREFERENCED_PARAMETER( Irql );

    DebugTrace( DBGLOG_CBDQ_CALLBACK,
                ("[%ws]: CbdqRelease\n",
                DRIVER_NAME) );

    ///  Get a pointer to the instance context.
    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    ///  Release the lock.
    ExReleaseFastMutex( &InstCtx->Lock );
}

NTSTATUS
CbdqInsertIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in PFLT_CALLBACK_DATA Data,
    __in_opt PVOID Context
    )
{
    PINSTANCE_CONTEXT InstCtx;
    PFLT_GENERIC_WORKITEM WorkItem = NULL;
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN WasQueueEmpty;

    UNREFERENCED_PARAMETER( Context );

    DebugTrace( DBGLOG_CBDQ_CALLBACK,
                ("[%ws]: CbdqInsertIo\n",
                DRIVER_NAME) );

    /// Get a pointer to the instance context.
    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    /// Save the queue state before inserting to it.
    WasQueueEmpty = IsListEmpty( &InstCtx->QueueHead );

    /// Insert the callback data entry into the queue.
    InsertTailList( &InstCtx->QueueHead,
                    &Data->QueueLinks );

    /// Queue a work item if no worker thread present.
    if (WasQueueEmpty
        && (1 == InterlockedIncrement( &InstCtx->WorkerThreadFlag )))
    {
        WorkItem = FltAllocateGenericWorkItem();
        if (WorkItem)
        {
            Status = FltQueueGenericWorkItem( WorkItem,
                                              InstCtx->Instance,
                                              PreIrpMjCreateWorkItemRoutine,
                                              DelayedWorkQueue,
                                              InstCtx->Instance );

            if (!NT_SUCCESS(Status))
            {
                DebugTrace( DBGLOG_CBDQ_CALLBACK | DBGLOG_ERR,
                            ("[%ws]: Failed to queue the work item (Status = "
                            "0x%x)\n",
                            DRIVER_NAME,
                            Status) );

                FltFreeGenericWorkItem( WorkItem );
            }

        }
        else
            Status = STATUS_INSUFFICIENT_RESOURCES;

        if (!NT_SUCCESS(Status))
        {
            /// Remove the callback data that was inserted into the queue.
            RemoveTailList( &InstCtx->QueueHead );
        }
    }

    return Status;
}

VOID
CbdqRemoveIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in PFLT_CALLBACK_DATA Data
    )
{
    UNREFERENCED_PARAMETER( DataQueue );

    DebugTrace( DBGLOG_CBDQ_CALLBACK,
                ("[%ws]: CbdqRemoveIo\n",
                DRIVER_NAME));

    ///  Remove the callback data entry from the queue.

    RemoveEntryList(&Data->QueueLinks);
}

PFLT_CALLBACK_DATA
CbdqPeekNextIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __in_opt PFLT_CALLBACK_DATA Data,
    __in_opt PVOID PeekContext
    )
{
    PINSTANCE_CONTEXT InstCtx;
    PLIST_ENTRY NextEntry;
    PFLT_CALLBACK_DATA NextData;

    UNREFERENCED_PARAMETER( PeekContext );

    DebugTrace( DBGLOG_CBDQ_CALLBACK,
                ("[%ws]: CbdqPeekNextIo\n",
                DRIVER_NAME) );

    ///  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
CbdqCompleteCanceledIo(
    __in PFLT_CALLBACK_DATA_QUEUE DataQueue,
    __inout PFLT_CALLBACK_DATA Data
    )
{
    PQUEUE_CONTEXT QueueCtx;

    UNREFERENCED_PARAMETER( DataQueue );

    DebugTrace( DBGLOG_CBDQ_CALLBACK,
                ("[%ws]: CbdqCompleteCanceledIo\n",
                DRIVER_NAME) );

    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( &g_FilterData.OueueContextLookaside,
                                 QueueCtx );
}

VOID
PreIrpMjCreateWorkItemRoutine(
    __in PFLT_GENERIC_WORKITEM WorkItem,
    __in PFLT_FILTER Filter,
    __in PVOID Context
    )
{
    PINSTANCE_CONTEXT   InstCtx =   NULL;
    PFLT_CALLBACK_DATA  Data    =   NULL;
    
    PFLT_INSTANCE Instance = (PFLT_INSTANCE)Context;
    PQUEUE_CONTEXT QueueCtx;
    NTSTATUS Status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;

    UNREFERENCED_PARAMETER(WorkItem);
    UNREFERENCED_PARAMETER(Filter);

    DebugTrace( DBGLOG_CBDQ_PRE_OPT,
                ("[%ws]: PreIrpMjCreateWorkItemRoutine\n",
                DRIVER_NAME) );

    /// 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 (;;)
    {
        callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;

        /// 因为没有办法不动挂住的队列, 这函数不干活
        PreIrpMjCreatePendIo(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];

            /// 通知r3裁决, 或自行判断
            PreIrpMjCreateProcessIo(Data, InstCtx, &callbackStatus);

            /// Check to see if we need to lock the user buffer.

            /// If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't 
            /// have to lock the buffer because its already a system buffer.

            /// If the MdlAddress is NULL and the buffer is a user buffer, 
            /// then we have to construct one in order to look at the buffer.

            /// If the length of the buffer is zero there is nothing to read,
            /// so we cannot construct a MDL.

            if ( !FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) && 
                 Data->Iopb->Parameters.Read.MdlAddress == NULL &&
                 Data->Iopb->Parameters.Read.Length > 0 )
            {
                Status = FltLockUserBuffer(Data);
                if (!NT_SUCCESS(Status))
                {
                    /// If could not lock the user buffer we cannot
                    /// allow the IO to go below us. Because we are 
                    /// in a different VA space and the buffer is a
                    /// user mode address, we will either fault or 
                    /// corrpt data

                    DebugTrace( DBGLOG_CBDQ_PRE_OPT | DBGLOG_ERR,
                                ("[%ws]: Failed to lock user buffer "
                                "(Status = 0x%x)\n",
                                DRIVER_NAME,
                                Status) );

                    callbackStatus = FLT_PREOP_COMPLETE;
                    Data->IoStatus.Status = Status;
                }
            }

            ///  Complete the I/O
            FltCompletePendedPreOperation( Data,
                                           callbackStatus,
                                           NULL );

            /// Free the extra storage that was allocated for this I/O.
            ExFreeToNPagedLookasideList( &g_FilterData.OueueContextLookaside,
                                         QueueCtx );
        }
        else
        {
            /// At this moment it is possible that a new IO is being inserted
            /// into the queue in the CbdqInsertIo routine. Now that the queue 
            /// is empty, CbdqInsertIo needs to make a decision on whether to 
            /// create
            /// a new worker thread. The decision is based on the race between
            /// the InterlockedIncrement in CbdqInsertIo 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 CbdqInsertIo 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 CbdqInsertIo
            ///     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
PreIrpMjCreatePendIo(
    __in PINSTANCE_CONTEXT InstanceContext
    )
{
    UNREFERENCED_PARAMETER(InstanceContext);
    
    /// 用户做裁决时, 不能动 InstanceContext->Cbdq
    /// 没有找到 FltCbdqGet之类的函数, 只有下面这些
    /*
    FltCbdqInitialize
    FltCbdqEnable
    FltCbdqDisable
    FltCbdqInsertIo
    FltCbdqRemoveIo
    FltCbdqRemoveNextIo 
    */

    /// 所以不能在这让用户裁决, 调整 : 在 PreIrpMjCreateProcessIo 做裁决

    return STATUS_SUCCESS;
}

NTSTATUS
PreIrpMjCreateProcessIo(
    PFLT_CALLBACK_DATA pData,
    PINSTANCE_CONTEXT pInstCtx,
    FLT_PREOP_CALLBACK_STATUS * pcbStatus)
{
    LARGE_INTEGER   DueTime;
    NTSTATUS        Status      =   STATUS_SUCCESS;
    PQUEUE_CONTEXT  pQueueCtx    =   NULL;

    DebugTrace(DBGLOG_CDO_OPT, (">> PreIrpMjCreateProcessIo\r\n"));
    /// 为了防止和r3操作 g_FilterData 产生冲突, 需要加锁
    LockResourceExclusive(&g_FilterData.ResourceWithR3);

    /// 判断 应用层是否传来了通讯通知的事件句柄
    /// 如果不能和r3通讯, 设置pData状态为(允许且没有后续回调)
    if (NULL == g_FilterData.g_pEventObject)
    {
        pData->IoStatus.Status = STATUS_SUCCESS;
        goto _PreIrpMjCreateProcessIo_END;
    }

    /// 将 pData 数据分享为一个结构, 带锁操作
    pQueueCtx = (PQUEUE_CONTEXT)pData->QueueContext[0];
    g_FilterData.g_pFileOptInfo = &pQueueCtx->fileInfo;

    /// 状态值初值(pQueueCtx.fileInfo.dwAnswerByR3)已经为允许,
    /// 如果R3没应答, 默认是放行的

    /// 复位队列等待事件
    g_FilterData.pTeardownEvent = &pInstCtx->TeardownEvent;
    KeResetEvent(&pInstCtx->TeardownEvent);

    /// 设置R3事件, 通知R3来拿
    KeSetEvent(g_FilterData.g_pEventObject, 0, FALSE);
    UnLockResource(&g_FilterData.ResourceWithR3);

    /// Delay R3_ANSWER_TIME_OUT_BY_SECOND seconds 
    /// or get signaled if the instance is torndown.
    DueTime.QuadPart = GetSecondCnt(R3_ANSWER_TIME_OUT_BY_SECOND);

    DebugTrace(DBGLOG_CDO_OPT, (">> KeWaitForSingleObject\r\n"));
    Status = KeWaitForSingleObject( &pInstCtx->TeardownEvent,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    &DueTime);
    DebugTrace(DBGLOG_CDO_OPT, ("<< KeWaitForSingleObject\r\n"));
    LockResourceExclusive(&g_FilterData.ResourceWithR3);
    
    /// 将状态值设置进pData, 由队列处理工作线程做后续处理
    /// 根据用户决断, pData 和 cbStatus 都要设置!!!
    pData->IoStatus.Status = ConvertR3Answer(
            pQueueCtx->fileInfo.dwAnswerByR3);
            
    *pcbStatus = (STATUS_ACCESS_DENIED == pData->IoStatus.Status) ?
                FLT_PREOP_COMPLETE : FLT_PREOP_SUCCESS_NO_CALLBACK;

    /// 数据已经被r3取过了, 清掉            
    /// r3接到通知后, 如果(NULL != g_FilterData.g_pFileOptInfo), 会来取数据
    g_FilterData.g_pFileOptInfo = NULL;
    g_FilterData.pTeardownEvent = NULL; ///< r3收完数据,不会通知驱动了

_PreIrpMjCreateProcessIo_END:
    UnLockResource(&g_FilterData.ResourceWithR3);
    return Status;
}

NTSTATUS ConvertR3Answer(DWORD dwAnswerByR3)
{
    NTSTATUS status =   STATUS_SUCCESS;
    
    switch(dwAnswerByR3)
    {
        case R3_ANSWER_REFUSE:
            status = STATUS_ACCESS_DENIED;
            break;
        
        case R3_ANSWER_PASS:
        default:
            status = STATUS_SUCCESS;
            break;
    }

    return status;
}

VOID
PreIrpMjCreateEmptyQueueAndComplete(
    __in PINSTANCE_CONTEXT InstanceContext
    )
{
    NTSTATUS status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;
    PFLT_CALLBACK_DATA Data;
    PQUEUE_CONTEXT QueueCtx;

    do {

        callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;

        Data = FltCbdqRemoveNextIo( &InstanceContext->Cbdq,
                                    NULL );

        if (Data)
        {
            QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];

            /// Check to see if we need to lock the user buffer.

            /// If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't 
            /// have to lock the buffer because its already a system buffer.

            /// If the MdlAddress is NULL and the buffer is a user buffer, 
            /// then we have to construct one in order to look at the buffer.

            /// If the length of the buffer is zero there is nothing to read,
            /// so we cannot construct a MDL.

            if ( !FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) && 
                 Data->Iopb->Parameters.Read.MdlAddress == NULL &&
                 Data->Iopb->Parameters.Read.Length > 0 )
            {
                status = FltLockUserBuffer( Data );
                if ( ! NT_SUCCESS( status ) )
                {
                    /// If could not lock the user buffer we cannot
                    /// allow the IO to go below us. Because we are 
                    /// in a different VA space and the buffer is a
                    /// user mode address, we will either fault or 
                    /// corrpt data
                   
                    callbackStatus = FLT_PREOP_COMPLETE;
                    Data->IoStatus.Status = status;
                }
            }

            FltCompletePendedPreOperation( Data,
                                           callbackStatus,
                                           NULL );

            ExFreeToNPagedLookasideList( &g_FilterData.OueueContextLookaside,
                                         QueueCtx );
        }

    } while (Data);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐