微过滤器简介

 

1. Filter管理器和Minifilter驱动架构

 

Filter管理器是一个内核模式驱动,它遵照legacy文件系统filter模型并暴露了FSFD中必需的一般功能。利用这些功能,第三方开发者可以写minifilter驱动,这样的驱动比legacy FSFD更易于开发,因此缩短了开发过程同时制造出更高质量、更灵活的驱动。


1.1 Filter管理器概念


Filter管理器随Windows一起被安装,但它只在一个minifilter驱动被加载时才会起作用。Filter管理器绑定到目标卷的文件系统栈上。Minifilter驱动为它要过滤的I/O操作而通过向filter管理器注册来间接绑定到文件系统栈上。

Legacy过滤驱动在文件系统I/O栈中的位置,相对于其他过滤驱动来说是在系统启动时由它的加载顺序组决定的。例如,一个antivirus过滤驱动在栈中的位置应该高于一个replication过滤驱动,因而它能在文件被复制到远程服务器之前探测病毒并消除文件中的病毒。因此,FSFilter Anti-Virus加载顺序组中的过滤驱动的加载要先于FSFilter Replication组中的过滤驱动。每一个加载顺序组都有一个相应的系统定义的类和INF文件中要使用的类GUID。

像legacy过滤驱动一样,minifilter驱动的绑定也按照一个特定的顺序。不过,绑定体的顺序是由一个叫altitude的唯一标识符决定的。Minifilter驱动在特定卷上的一个特定altitude的绑定体被称为这个minifilter驱动的一个instance。

Minifilter驱动的altitude确保它的实例总是在相应的位置被加载,它决定filter管理器以什么顺序来调用minifilter驱动来处理I/O。Altitudes由微软分配和管理。
下图展示了一个用filter管理器简化后的I/O栈和三个minifilter驱动。

 


用Filter管理器简化后的I/O栈和Minifilter驱动

 

Minifilter驱动能过滤IRP-I/O操作、fast I/O和FSFilter callback操作。对每一个它要过滤的I/O操作它都可以注册一个pre-oper callback例程,一个post-oper callback 例程或二者都注册。当处理一个I/O操作时,filter管理器调用每个minifilter为此操作注册的callback例程。当该callback例程返回时,filter管理器调用下一minifilter驱动为此操作注册的callback例程。

举个例子,假设上图中的三个minifilter驱动都为同一I/O操作注册了callback例程,则filter管理器会以altitude由高到低的顺序(A,B,C)调用它们的pre-oper callback例程,然后把此I/O操作转寄给下一较低级的驱动进一步处理。当filter管理器接收到此I/O请求的完成时,它以相反的顺序即由低到高(C,B,A)调用每一个minifilter驱动的post-oper callback例程。

为了同legacy过滤驱动协同工作,filter管理器可以绑定filter设备对象到同一个文件系统I/O栈的多个位置。每一个filter管理器的filter设备对象都叫做一个frame。从legacy过滤驱动的角度来说,每一个filter管理器frame仅仅是另外的一个legacy过滤驱动。

每一个filter管理器frame都表示一个altitude区间。如果某个legacy过滤驱动被绑定到文件系统栈上,系统就为在该legacy过滤驱动之上或之下的altitude范围绑定一个filter管理器frame,来确保minifilter驱动绑定到正确的位置上。

如果某个minifilter驱动被卸载并重加载,则它会被重加载到同一frame(它从其中被卸载)中的同一altitude上。


用两个Filter管理器Frame和一个legacy过滤驱动简化后的I/O栈

 

1.2 Filter管理器模型的优势

 

Filter管理器模型在现有的legacy过滤驱动模型之上提供了以下优势:

比filter加载顺序更易控制. 不像legacy过滤驱动,一个minifilter驱动可以在任何时候被加载且因其altitude被绑定到合适的位置。

 

在系统运行期间的卸载能力. 不像在系统运行期间不能被卸载的legacy过滤驱动,minifilter驱动能在任意时间被卸载且必要时它能阻止它自己被卸载。Filter管理器同步所有minifilter驱动绑定体的安全移除,且它会处理minifilter驱动被卸载之后完成的操作。

仅需处理必要操作的能力. Filter管理器使用callback模型,在这个模型中minifilter驱动可以挑选要过滤的I/O操作的类型(基于IRP的, fast I/O或FSFilter)。Minifilter驱动只接收它已经为其注册了callback例程的I/O操作。Minifilter驱动可以注册一个唯一的pre-oper或post-oper callback例程,或两者都注册,它可以忽略某些类型的操作,比如分页I/O和缓存的I/O。

 

内核栈的应用更加高效. Filter管理器被优化为减少了它所使用的内核栈的数量,callback模型也大大减少了栈上的minifilter驱动之间的冲突。Filter管理器通过支持filter初始化的I/O(这仅能被栈中lower驱动看见)减少了递归I/O的冲突。

 

冗余代码更少. Filter管理器用多种方法减少了minifilter驱动所必需的代码量,比如提供名产生操作的基础架构和为多个minifilter驱动的使用缓存文件名。 Filter管理器绑定卷并通知minifilter驱动什么时候卷可用。Filter管理器被优化为支持多处理器系统,这使得锁定更高效且更低错误的可能性。

 

简化了复杂部分. Filter管理器通过为一般功能(像命名,上下文管理,在用户模式和内核模式之间通信,屏蔽文件系统的不同)提供支持例程简化了I/O请求的过滤。Filter管理器代表minifilter驱动处理某些任务,比如 pending IRP和枚举并绑定到文件系统栈。

 

更易于添加新操作. 因为minifilter驱动仅为其将要处理的I/O操作注册,对新操作的支持可以在不破坏现有minifilter驱动的前提下被添加到filter管理器中。

 

对多个平台的更好支持. Minifilter驱动可以运行在支持filter管理器的一切版本的Windows上。如果某个minifilter驱动为一个在运行时不可用的I/O操作注册了,则filter管理器只是不对该操作调用minifilter驱动。Minifilter驱动可以以程序的方式决定函数是否可用,filter管理器结构被设计为可扩展的。

 

对用户模式应用程序的更好支持. Filter管理器为与minifilter驱动一起工作的用户模式服务及控制程序提供一般功能。Filter管理器用户模式库, Filterlib.dll, 激活了用户模式服务或控制程序同minifilter驱动之间的通信。Filterlib.dll 也为管理工具提供了接口。

1.3 Minifilter驱动的Filter管理器支持


本节描述filter管理器为minifilter驱动执行的一般任务提供的支持:


1.3.1 Loading and Unloading


Minifilter驱动能在系统运行期间的任意时刻被加载。如果minifilter驱动的INF file 指定其驱动启动类型为SERVICE_BOOT_START, SERVICE_SYSTEM_START或SERVICE_AUTO_START中的一个,那么minifilter驱动的加载就会依照现有的FSFD加载顺序组定义来支持与legacy过滤驱动间的互用性。在系统运行期间,minifilter能通过一个服务启动请求(sc start, net start或者服务APIs)或通过一个外部的加载请求(fltmc load, FltLoadFilter或FilterLoad) 被加载。

当minifilter驱动被加载时,它的DriverEntry 例程会被调用,因此minifilter驱动可以执行将应用于该minifilter驱动的所有实例的初始化操作。在它的DriverEntry 例程中它调用FltRegisterFilter来向filter管理器注册callback例程,调用FltStartFiltering来通知filter管理器它已经准备好开始绑定到卷并过滤I/O请求了。

Minifilter驱动实例在INF file被定义来用于安装这个minifilter驱动。Minifilter驱动的 INF文件必须定义一个默认实例,它也可以定义额外的实例。这些定义适用于所有的卷。每一个实例的定义都包括实例名,它的altitude和表示这个实例是否可以被自动地、手动地或二者都有地绑定的标记。默认实例用于顺序化minifilter驱动以便filter管理器能以正确的顺序调用minifilter驱动的挂载和实例setup例程。当调用者不指定一个实例名时也用外在的绑定请求来使用默认实例。

在卷挂载后,Filter管理器在首个create操作上调用它的InstanceSetupCallback例程来自动地通知minifilter驱动有一个可用卷。当filter管理器在系统启动时枚举现有卷时,这会在FltStartFiltering返回之前发生。当某个卷被挂载或作为一个外部绑定请求 (fltmc attach, FltAttachVolume或FilterAttach)的结果时,那也会在运行时期间发生。

当minifilter驱动被卸载,其所绑定的卷被停止或作为一个外部断开请求(fltmc detach, FltDetachVolume或FilterDetach)的结果时,一个minifilter驱动实例会被拆卸。如果minifilter驱动注册了一个InstanceQueryTeardownCallback 例程,它可以通过调用FilterDetach或FltDetachVolume来令一个外部断开请求失败。拆卸的过程如下:

如果minifilter驱动注册了一个InstanceTeardownStartCallback callback例程,那么filter管理器就会在这个过程的开始调用它。在此例程汇中,minifilter驱动应该完成所有未决操作,取消或完成其他工作(比如由minifilter驱动产生的I/O请求和停止队列化新的工作项)。

在实例拆卸期间,一切当前正在执行的pre-oper或post-oper callback 例程继续正常的处理,一切正在等待post-oper callback的I/O操作会被"耗尽" 或取消,一切由minifilter驱动产生的I/O操作继续正常的处理直到它们被完成。

如果minifilter驱动注册了一个InstanceTeardownCompleteCallback 例程,  filter管理器在所有未决I/O操作已经完成之后调用该例程。在此例程中,minifilter 驱动关闭一切仍然在open的文件。

在实例的所有未决引用都被消耗之后,filter管理器删除残存的context而实例也完全被拆卸。

在系统正在运行时,minifilter驱动能通过一个服务停止请求(sc stop, net stop或服务APIs)或通过一个外部卸载请求(fltmc unload, FltUnloadFilter或FilterUnload)被卸载.

Minifilter驱动的FilterUnloadCallback 例程在minifilter驱动卸载时被调用。此例程关闭一切open通信服务器端口,调用FltUnregisterFilter并执行一切需要的清除。该例程的注册是可选的。不过,如果minifilter驱动没有注册一个FilterUnloadCallback 例程, 则minifilter驱动不能被卸载.更多此例程的信息可参考Writing a FilterUnloadCallback Routine.

用于加载和卸载Minifilter驱动的Filter管理器例程

Filter管理器为外部的加载和卸载请求提供了以下支持例程(可从用户模式或内核模式发出):

FilterLoad
    FilterUnload
    FltLoadFilter
    FltUnloadFilter

以下例程用于为实例的setup和拆卸而注册和注销callback例程:

FltRegisterFilter
    FltStartFiltering
    FltUnregisterFilter

用于实例setup,拆卸和卸载的Minifilter驱动Callback例程
以下minifilter驱动callback例程存储于FLT_REGISTRATION 结构之中,这个结构作为一个参数被传给FltRegisterFilter:
     Callback例程名                            Callback例程类型
     InstanceSetupCallback                     PFLT_INSTANCE_SETUP_CALLBACK
     InstanceQueryTeardownCallback             PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK
     InstanceTeardownStartCallback             PFLT_INSTANCE_TEARDOWN_CALLBACK
     InstanceTeardownCompleteCallback          PFLT_INSTANCE_TEARDOWN_CALLBACK
     FilterUnloadCallback                      PFLT_FILTER_UNLOAD_CALLBACK

 

1.3.2 .处理I/O操作


Filter管理器简化了minifilter驱动对I/O操作的处理。legacy过滤驱动必须正确地将所有I/O请求都传给lower驱动并正确地处理pending请求,同步和I/O完成而不论它有没有做该请求相关的工作,但minifilter驱动仅当它必须处理该I/O操作时它才注册。

对一个给定的I/O操作来说,只有已经为该I/O操作注册了一个pre-oper callback例程的minifilter驱动才会被Filter管理器调用。Filter管理器也代表minifilter驱动处理某些IRP维护任务,比如复制参数到下一栈位置并传播IRP的PendingReturned标记.

在其pre-oper callback例程中,只要是需要对I/O操作进行的处理minifilter驱动都做,而且它会通过在该例程中返回合适的值来指示对这个IRP的什么工作将要被完成。例如,为了不借助完成例程而转寄IRP给下一lower驱动,minifilter驱动就会返回 FLT_PREOP_SUCCESS_NO_CALLBACK;为了借助完成例程来做同样的事情(对此I/O操作的minifilter驱动post-oper callback例程),它就会返回 FLT_PREOP_SUCCESS_WITH_CALLBACK.

在其pre-oper callback例程中,如果需要的话minifilter驱动可以通过调用FltQueueDeferredIoWorkItem将操作queue到一个worker线程。在那之后,minifilter驱动会从它的pre-oper callback 例程中返回FLT_PREOP_PENDING来指示I/O操作正处于pending状态而它要负责该操作的完成或者负责恢复对这个请求的处理。为了恢复处理,minifilter驱动要从工作者线程中调用FltCompletePendedPre-oper.

如果minifilter驱动需要维护它自己的per-instance安全取消要被处理的未决I/O操作的队列,它可以在它的InstanceSetupCallback 例程中调用FltCbdqInitialize 来设立这样一个队列并在需要时在其pre-oper callback 例程中调用FltCbdqInsertIo 来把I/O操作插入到此队列中。

当lower过滤驱动(legacy filters和minifilter驱动s)已经结束了完成处理时,filter管理器会调用一个minifilter驱动的 post-oper callback 例程.

在其post-oper callback例程中,minifilter驱动可以调用FltDoCompletionProcessingWhenSafe 来确保在安全的IRQL处理某个I/O操作的完成处理。否则它可以通过调用FltQueueDeferredIoWorkItem 来queue完成处理到一个worker线程。在此之后,minifilter驱动会从其post-oper callback例程中返回FLT_POSTOP_MORE_PROCESSING_REQUIRED来停止Filter管理器对此I/O操作的完成处理。要恢复完成处理,minifilter驱动得从worker线程中调用 FltCompletePendedPost-oper.

Filter管理器为"一般"工作项(关联一个minifilter驱动或minifilter驱动实例而非一个I/O操作的工作项)的队列化提供支持。Minifilter驱动要插入一个工作项到一个系统工作队列中可以通过调用FltQueueGenericWorkItem。此例程和ExQueueWorkItem类似; 例如,工作项(通过调用FltAllocateGenericWorkItem分配)可以被重用.不过,对minifilter驱动来说使用FltQueueGenericWorkItem 会更安全,因为在未决工作项仍处于正被处理状态期间filter管理器不允许minifilter驱动或minifilter驱动实例的卸载。

Filter管理器也为oplock操作提供支持。为了oplock操作,minifilter驱动可以使用像FltInitializeOplock 和FltOplockFsctrl(与文件系统和legacy过滤驱动所使用的FsRtlInitializeOplock和FsRtlOplockFsctrl例程等价).

用于处理I/O操作的Filter管理器例程

Filter管理器为pre-oper和post-oper callback例程中的pending I/O提供以下支持例程:

FltCompletePendedPost-oper
    FltCompletePendedPre-oper
    FltDoCompletionProcessingWhenSafe

以下例程用于queue pre-oper和post-oper callback例程中的工作项:

FltAllocateDeferredIoWorkItem
    FltAllocateGenericWorkItem
    FltFreeDeferredIoWorkItem
    FltFreeGenericWorkItem
    FltQueueDeferredIoWorkItem
    FltQueueGenericWorkItem

下列例程提供安全取消队列支持:

FltCbdqDisable
    FltCbdqEnable
    FltCbdqInitialize
    FltCbdqInsertIo
    FltCbdqRemoveIo
    FltCbdqRemoveNextIo

下列例程提供oplock支持:

FltCheckOplock
    FltCurrentBatchOplock
    FltInitializeOplock
    FltOplockFsctrl
    FltOplockIsFastIoPossible
    FltUninitializeOplock

用于处理I/O操作的Minifilter驱动Callback例程:

以下callback例程存储于FLT_OPERATION_REGISTRATION 结构之中,用于minifilter驱动处理的任何一个类型的I/O操作:
     Callback例程名            Callback例程类型
     Post-oper                 PFLT_POST_OPERATION_CALLBACK
     Pre-oper                  PFLT_PRE_OPERATION_CALLBACK

 

1.3.3 修改参数


Minifilter驱动可以修改关联一个I/O操作的某些参数,比如目标实例,目标FileObject和操作指定的参数(包括buffer地址和MDL地址)。minifilter驱动通常在其pre-oper callback中修改这些参数。如果它修改参数,它必须调用FltSetCallbackDataDirty 通知filter管理器:这些参数已经改变了。它也应该在其pre-oper callback的context中记录这些改变,以便它们在其post-oper callback中可用。

当minifilter驱动在它的pre-oper callback中完成某个I/O操作或在它的post-oper callback中令操作失败(比如把STATUS_SUCCESS改为一个错误的状态)时,它可以改变这个操作的I/O状态。在此情形下,不必调用FltSetCallbackDataDirty.

更多关于修改参数的信息可参考Modifying the Parameters for an I/O Operation一文.

minifilter驱动可以通过用自己的buffer替代I/O操作的buffer域来实现"交换buffers". 这样的minifilter驱动负责同步地保存I/O请求的MDL和buffer域. Filter管理器设置FLT_CALLBACK_DATA 中的FLTFL_CALLBACK_DATA_SYSTEM_BUFFER_FLAG来指示一个buffer是否为一个系统buffer;如果是,minifilter驱动必须从非分页池中分配替代buffer并把MDL域设为NULL.否则,buffer可以从分页或非分页池中分配,而minifilter驱动必须总是创建和设置MDL. (在fast I/O操作的情形中,新的buffer可以从分页或非分页池中分配且MDL应该为NULL.) minifilter驱动不能释放它替代的buffer或MDL,也不能释放它已经成功地插入到一个callback数据结构中的一切MDL (filter管理器会代表minifilter驱动释放MDL). 在对一个MDL或buffer做出改变之后,minifilter驱动必须调用FltSetCallbackDataDirty.

minifilter驱动必须为它要在其中交换buffer的操作注册一个post-oper callback.在这个callback例程中,minifilter驱动必须释放它分配的一切buffer.filter管理器会释放MDL除非minifilter驱动调用FltRetainSwappedBufferMdlAddress;在此情形下, minifilter驱动负责释放MDL.minifilter驱动要获得在其pre-oper callback中设置的buffer的MDL可以调用 FltGetSwappedBufferMdlAddress.

如果minifilter驱动在某个操作中已经交换了buffer,而在此操作期间它被卸载,则该操作不能被"耗尽";取而代之的是,此操作会被取消而filter管理器在卸载这个minifilter驱动之前要等待该操作的完成.

SwapBuffers例子可以作为一个交换buffer的minifilter驱动的例子。

用于修改参数的Filter管理器例程:

Filter管理器为在pre-oper和post-oper callback例程中修改I/O操作参数提供下列支持例程:

FltClearCallbackDataDirty
    FltIsCallbackDataDirty
    FltSetCallbackDataDirty

下列例程为交换buffer提供支持:

FltGetSwappedBufferMdlAddress
    FltRetainSwappedBufferMdlAddress


1.3.4 访问User Buffers


指定给一个给定I/O操作的所有参数(包括buffer和MDL)都被定义在一个FLT_PARAMETERS 联合体中.此联合体包含在一个FLT_IO_PARAMETER_BLOCK 结构中,此结构通过表示I/O操作的FLT_CALLBACK_DATA 结构的Iobp 成员来访问。filter管理器和minifilter驱动都使用FLT_CALLBACK_DATA 结构来初始化和处理I/O操作。

FLT_PARAMETERS联合体也包含给基于IRP的操作的参数定义,这些参数由该操作使用的buffering方式(buffered,direct I/O或者neither)指定. 它也包含非基于IRP的操作的参数定义(fast I/O和FsFilter callback例程).

minifilter驱动可以调用FltDecodeParameters 来获得MDL地址的指针,buffer指针,buffer长度和一个I/O操作期望的访问参数。这使得minifilter驱动拥有了一个切换声明来在(跨多个I/O操作访问这些参数的)帮助例程中找到这些参数的位置。

当处理一个调用了user buffer的I/O操作时,如果MDL可用则minifilter驱动应总是使用MDL。若如此,minifilter驱动应调用MmGetSystemAddressForMdlSafe 来获得MDL的系统地址并用这个系统地址来访问user buffer.

若只有buffer地址可用,则minifilter驱动应总是在try/except块中封装对buffer的一切访问意图。如果minifilter驱动需要在一个不同步的post-oper callback例程中访问buffer,或者如果I/O操作被发给一个worker线程,则minifilter驱动也应该通过调用FltLockUserBuffer锁住user buffer.此函数基于I/O操作的类型决定要请求锁住的buffer所采用的访问方式并创建一个指向已锁页的MDL。

用于访问User Buffers 的Filter管理器例程:

filter管理器为在pre-oper和post-oper callback例程中访问user buffer提供了以下支持例程:
    FltDecodeParameters
    FltLockUserBuffer


1.3.5 管理文件名


filter管理器消除了legacy过滤驱动重获和管理文件名所必需的许多工作。当一个名被请求时,filter管理器在引用计数结构中以适当的格式为当前操作提供名: 规范名, opened名或短名.

minifilter驱动可以调用FltGetDestinationFileNameInformation 来为正被rename或其NTFS hard link正被创建的文件或目录构造一个全路径名。这个名可以以规范的或打开的文件的格式返回。

filter管理器也为重获规范文件名信息(由于文件名tunneling,该信息无效)提供FltGetTunneledName 例程。

为提高性能,filter管理器把名放到一个cache中,该cache在系统中的所有minifilter驱动之间可能是共享的。

修改名字空间的Minifilter驱动可以通过注册callback例程来截取名字查询操作从而利用filter管理器对名提供者的支持,比如upper minifilter驱动产生或规范化一个名的请求.

用于名管理的Filter管理器例程

filter管理器为名字管理提供以下支持例程:

FltGetDestinationFileNameInformation
    FltGetFileNameInformation
    FltGetFileNameInformationUnsafe
    FltGetTunneledName
    FltParseFileNameInformation
    FltReleaseFileNameInformation

用于名管理的Minifilter驱动Callback例程

以下callback例程存储于FLT_REGISTRATION结构中,此结构作为一个参数被传给FltRegisterFilter, 用于minifilter驱动修改名字空间:
       Callback例程名                     Callback例程类型
       GenerateFileNameCallback           PFLT_GENERATE_FILE_NAME
       NormalizeContextCleanupCallback    PFLT_NORMALIZE_CONTEXT_CLEANUP
       NormalizeNameComponentCallback     PFLT_NORMALIZE_NAME_COMPONENT


1.3.6 管理Contexts


filter管理器使minifilter驱动能把上下文与object关联起来,来跨I/O操作保持状态。有上下文的Object包括卷,实例,流和流句柄.

第三方文件系统必须使用FSRTL_ADVANCED_FCB_HEADER 结构(取代 FSRTL_COMMON_FCB_HEADER结构)来适应流和流句柄context.

上下文(除了卷上下文,它必须从非分页池中分配)可以从分页或非分页池中分配。
当所有未决引用已经被释放时,上下文被自动释放。如果minifilter驱动定义了一个上下文cleanup callback例程,filter管理器就会在上下文被释放之前调用该例程。上下文以此顺序cleanup: 流句柄,流,实例和卷。

当关联的Object被删除,当实例被断开或当minifilter驱动被卸载时,filter管理器负责删除上下文。

如果某个minifilter驱动对每个卷仅支持一个实例,那它应该使用实例上下文而非卷上下文来获得更佳性能。minifilter驱动也可以通过在流或流句柄上下文中存储指向minifilter驱动实例上下文的指针来提高性能。

分页文件的上下文不被支持,以下操作期间也不支持上下文:

Create请求的pre-oper处理

close请求的Post-oper处理

IRP_MJ_NETWORK_QUERY_OPEN请求的处理

看CTX例子,它是一个使用上下文的minifilter驱动.

用于上下文管理的Filter管理器例程

filter管理器为创建、注册和设置上下文提供以下支持例程:

FltAllocateContext
    FltRegisterFilter
    FltSetInstanceContext
    FltSetStreamContext
    FltSetStreamHandleContext
    FltSetVolumeContext

以下例程用于查询上下文支持:

FltSupportsStreamContexts
    FltSupportsStreamHandleContexts

以下例程用于获得和引用上下文:

FltGetContexts
    FltGetInstanceContext
    FltGetStreamContext
    FltGetStreamHandleContext
    FltGetVolumeContext
    FltReferenceContext

以下例程用于释放和删除上下文:

FltDeleteContext
    FltDeleteInstanceContext
    FltDeleteStreamContext
    FltDeleteStreamHandleContext
    FltDeleteVolumeContext
    FltReleaseContext
    FltReleaseContexts

用于context管理的Minifilter驱动Callback例程

以下callback例程存储于FLT_REGISTRATION 结构中,此结构作为一个参数被传给FltRegisterFilter, 用于minifilter驱动管理上下文:
    Callback例程名              Callback例程类型
    ContextAllocateCallback     PFLT_CONTEXT_ALLOCATE_CALLBACK
    ContextCleanupCallback      PFLT_CONTEXT_CLEANUP_CALLBACK
    ContextFreeCallback         PFLT_CONTEXT_FREE_CALLBACK


1.3.7 Minifilter驱动产生的I/O请求


minifilter驱动可以从它自己在当前卷或另一个卷上的实例中产生并发送基于IRP的I/O操作。此I/O操作仅minifilter驱动实例,绑定位置在此实例之下的legacy过滤驱动和文件系统可见。这解决了许多在legacy过滤驱动模型中递归I/O相关的问题,在该模型中,被legacy过滤驱动产生的I/O请求必须从顶部驱动开始遍历整个文件系统栈。

在某个minifilter驱动的所有未决I/O操作被完成之前,filter管理器不能卸载它。

用于由Minifilter驱动产生的I/O请求的Filter管理器例程

filter管理器为创建、打开、读写文件提供了以下支持例程:

FltClose
    FltCreateFile
    FltCreateFileEx
    FltReadFile
    FltWriteFile

以下例程用于设置和移除重解析点:

FltTagFile
    FltUntagFile

以下例程用于产生I/O请求:

FltAllocateCallbackData
    FltFreeCallbackData
    FltPerformAsynchronousIo
    FltPerformSynchronousIo
    FltReuseCallbackData

以下例程用于取消文件open请求并重发一个I/O请求:

FltCancelFileOpen
    FltReissueSynchronousIo

filter管理器也提供下列多种用途的例程:

FltDeviceIoControlFile
    FltFlushBuffers
    FltFsControlFile
    FltQueryInformationFile
    FltQuerySecurityObject
    FltQueryVolumeInformationFile
    FltSetInformationFile
    FltSetSecurityObject


1.3.8 用户模式和内核模式间的通信


filter管理器通过通信端口支持用户模式和内核模式之间的通信。minifilter驱动通过指定要被应用于通信端口object的一个安全描述符来控制端口上的安全。端口通信不是buffered,因此它更快更高效。用户模式应用程序或服务可以回答来自minifilter驱动的消息从而双向通信.

当minifilter驱动创建了一个通信服务器端口时,它暗中开始收听端口上引入的连接。当某个用户模式调用者试图连接到该端口时,filter管理器用新创连接的一个句柄调用minifilter驱动的 ConnectNotifyCallback 例程。当filter管理器恢复控制时,它传给用户模式调用者一个表示用户模式调用者对这个连接的endpoint的一个单独的文件句柄。该句柄可用于把I/O完成端口和收听者端口关联起来。

仅当用户模式调用者在端口上已拥有了安全描述符指定的权限时,一个连接才会被同意。对端口的每一个连接都有它自己的消息队列和私有endpoint。

任何一个(内核或用户的)endpoint的关闭都会结束相应的连接。当某个用户模式调用者关闭endpoint句柄时,filter管理器调用minifilter驱动的DisconnectNotifyCallback 例程以便minifilter驱动可以关闭连接句柄。

通信服务器端口的关闭会阻止新连接但不会结束现有的连接。当minifilter驱动卸载时filter管理器就会结束现有的连接。

用于在用户模式和内核模式间通信的Filter管理器例程

filter管理器为内核模式minifilter驱动与用户模式应用程序的通信提供了以下支持例程:

FltCloseClientPort
    FltCloseCommunicationPort
    FltCreateCommunicationPort
    FltSendMessage

以下例程用于用户模式应用程序与minifilter驱动通信:

FilterConnectCommunicationPort
    FilterGetMessage
    FilterReplyMessage
    FilterSendMessage

用于在用户模式和内核模式间通信的Minifilter驱动Callback例程

以下minifilter驱动的callback例程作为参数被传给FltCreateCommunicationPort:
    Callback例程名              Callback例程类型
    ConnectNotifyCallback       PFLT_CONNECT_NOTIFY
    DisconnectNotifyCallback    PFLT_DISCONNECT_NOTIFY
    MessageNotifyCallback       PFLT_MESSAGE_NOTIFY


1.3.9 User-Mode Library


filter管理器用户模式接口为包括过滤驱动在内的产品提供了一般功能。用户模式library是Fltlib.dll. 应用程序包括头文件FltUser.h和FltUserStructures.h, 和对FltLib.lib的链接.

这些用户模式接口激活了对minifilter驱动的全面控制和用户模式服务或控制程序与过滤驱动间的通信。用户模式接口也为管理工具提供了接口来允许对filters,卷和实例的枚举。

Filter管理器用户模式Library例程

filter管理器为加载和卸载minifilter驱动提供了以下支持例程:

FilterLoad
FilterUnload

以下例程用于创建和关闭minifilter驱动及实例句柄:

FilterClose
    FilterCreate
    FilterInstanceClose
    FilterInstanceCreate

以下例程用于绑定和断开minifilter驱动实例:

FilterAttach
    FilterAttachAtAltitude
    FilterDetach

以下例程用于枚举filters,卷和实例:

FilterFindFirst
    FilterFindNext
    FilterInstanceFindFirst
    FilterInstanceFindNext
    FilterVolumeFindFirst
    FilterVolumeFindNext
    FilterVolumeInstanceFindFirst
    FilterVolumeInstanceFindNext

以下例程用于查询信息:

FilterGetDosName
    FilterGetInformation
    FilterInstanceGetInformation

以下例程用于用户操作发起通信:

FilterConnectCommunicationPort
    FilterSendMessage

以下例程用于minifilter驱动响应通信:

FilterGetMessage
    FilterReplyMessage


1.4 控制Filter管理器操作


在早于Vista的Windows中,filter管理器的操作由注册表中下列REG_DWORD AttachWhenLoaded值控制:

HKLM\System\CurrentControlSet\Services\FltMgr

当AttachWhenLoaded被设置为零时,filter管理器不会绑定到任何卷上直到某个minifilter驱动向filter管理器注册.当AttachWhenLoaded被设置为1时,filter管理器在引导时绑定到所有卷上。

2K SP4及以后或XP SP2及更高版本上AttachWhenLoaded的默认值为零。2K3 SP1及后来版本上AttachWhenLoaded默认值为1.

Vista中AttachWhenLoaded值不存在.

当minifilter驱动安装到Vista之前的Windows版本上,软件安装器应置AttachWhenLoaded为1.若修改前AttachWhenLoaded的值为零,则安装器应在minifilter驱动安装之后重启系统.


1.5 开发和测试工具


本节描述的filter管理器工具在2K3 SP1的IFS Kit和Vista及以后版本的OS中提供了.

也鼓励Minifilter驱动开发者使用多用途的内核模式开发和测试工具,比如带驱动指定的规则的PREfast.


1.5.1 Fltmc.exe 控制程序


Fltmc.exe控制程序是common minifilter驱动管理操作的命令行应用. 开发者可以使用Fltmc.exe来加载和卸载minifilter驱动,绑定minifilter驱动到卷或从卷断开,以及枚举minifilter驱动、实例和卷.

!fltkd Debugger Extension

!fltkd调试器扩展命令在Debugging Tools for Windows中已经提供了.常用命令如下:
命令
描述
!cbd
filter管理器等价于!irp
!filter
列出指定的filter的详细信息
!filters
列出所有已绑定的minifilter驱动
!frames
列出所有filter管理器frame和已绑定的minifilter驱动
!instance
列出指定的实例的详细信息
!volume
列出指定卷的详细信息
!volumes
列出所有卷和已绑定的minifilter驱动实例的详细信息

更多额外的调试帮助,要用调试版本的Fltmgr.sys测试minifilter驱动,它包含众多的 ASSERT来捕获一般错误.


1.5.2 Filter Verifier


Filter Verifier是Driver Verifier 中的一个I/O Verification 选项,它验证minifilter驱动对filter管理器函数的使用. 安装Filter Verifier要用filter管理器.开发者应总是开发minifilter驱动时使用Driver Verifier和Filter Verifier.

要使用Filter Verifier,需指定minifilter驱动的名并激活Driver Verifier(Verifier.exe)中的I/O Verification选项.当minifilter驱动向filter管理器注册时校验就会启动.
Filter Verifier在minifilter驱动中的有效用法如下:

纠正参数的使用和context的调用

纠正pre-oper和post-oper callback例程的返回值

使callback数据中参数的改变一致而连贯

Filter Verifie跟踪以下filter管理器objects:

Contexts

Callback数据结构

Queued Work Items

NameInformation结构

File Objects

Filter Objects

实例Objects

卷Objects


1.6 嵌入Legacy过滤驱动的指导方针


微软鼓励开发者嵌入legacy过滤驱动到filter管理器模型中来获得更好的性能提高系统稳定性. 有经验的开发者会发现把legacy过滤驱动嵌入到minifilter驱动中相对容易。微软的过滤驱动开发者被推荐用以下方法:

首先用一套可靠的衰退测试来验证legacy过滤驱动和被嵌入的 minifilter驱动之间的行为.

创建一个minifilter驱动外壳并系统地从legacy过滤驱动中移植功能到minifilter驱动.例如,令attachment保持工作,然后每次嵌入一个操作,在操作之后进行测试。

最后改变用户模式/内核模式通信,以便你可以使用现有工具来测试minifilter驱动.

用PREfast编译并激活Driver Verifier中的Filter Verifier I/O verification选项来测试.

在嵌入过程期间,你应该回顾所有的legacy过滤驱动代码来充分利用filter管理器的能力.尤其是要记住下面的:

如果合适基于IRP I/O和fast I/O的操作用相同的操作,这会减少代码的复制.

当注册操作时,minifilter驱动可以明确选择忽视所有的分页I/O和cached I/O,出去检查这些的代码.

实例通知大大简化了attach/detach逻辑.

仅注册minifilter驱动必须处理的操作;你可以忽视其他所有的事情.

利用filter管理器context和名管理支持.

利用filter管理器对发出非递归I/O的支持.

minifilter驱动不能以来局部变量来维护从pre-oper处理到post-oper处理的context.可以分配一个lookaside列表来存储操作状态.

当一个名或context结束时确定释放了引用.

用户模式中的完成端口为构造队列添加了一项强大的技术。你大概只需要一个单一的连接到一个单一的已命名的端口.

下表列出了legacy过滤驱动中的一般操作和它们是如何映射到filter管理器模型的.

 

 Legacy 过滤驱动模型

 Filter管理器模型

 没有完成例程的Pass-through操作

 如果你的minifilter驱动从不为这种类型的I/O操作工作,则无需为其注册pre-operpost-oper callback例程

否则,从为此操作注册pre-oper callback例程中返回 FLT_PREOP_SUCCESS_NO_CALLBACK.

参见Returning FLT_PREOP_SUCCESS_NO_CALLBACK一文

 带有完成例程的Pass-through操作

 pre-oper callback例程中返回 FLT_PREOP_SUCCESS_WITH_CALLBACK.Returning FLT_PREOP_SUCCESS_WITH_CALLBACK.

 在 pre-oper callback 例程中pend操作

 需要时调用FltLockUserBuffer来确保一切user buffer都被锁住,以便在一个worker线程中它们能被访问

通过调用像FltAllocateDeferredIoWorkItem和 FltQueueDeferredIoWorkItem这样的支持例程queue工作到一个为worker线程.

pre-oper callback例程中返回FLT_PREOP_PENDING.

当准备好返回I/O操作到filter管理器时,调用FltCompletePendedPre-oper.

参见Pending an I/O Operation in a Pre-oper Callback Routine.

 post-oper callback例程中pend操作

 pre-oper callback例程中,调用 FltLockUserBuffer来确保user buffers已经被锁住,以便在一个worker线程中它们能被访问

通过调用像FltAllocateGenericWorkItem FltQueueGenericWorkItem这样的支持例程来queue工作到一个worker线程.

post-oper callback例程中返回 FLT_POSTOP_MORE_PROCESSING_REQUIRED.

当准备好返回I/O操作给filter管理器时,调用 FltCompletePendedPost-oper.

参见Pending an I/O Operation in a Post-oper Callback Routine.

 操作同步

 pre-oper callback例程中返回 FLT_PREOP_SYNCHRONIZE. Returning FLT_PREOP_SYNCHRONIZE

 在 pre-oper callback例程中完成操作

 设置最终的操作状态和信息到该操作的FLT_CALLBACK_DATA 结构的成员IoStatus 

pre-oper callback 例程中返回 FLT_PREOP_COMPLETE.

参见Completing an I/O Operation in a Pre-oper Callback Routine.

 在操作已经于pre-oper callback例程中被pend之后完成该操作

 设置最终的操作状态和信息到该操作的FLT_CALLBACK_DATA 结构的成员IoStatus 

worker线程中调用FltCompletePendedPre-oper 来处理I/O操作,以CallbackStatus参数的形式传递FLT_PREOP_COMPLETE.

参见Completing an I/O Operation in a Pre-oper Callback Routine.

 在完成例程中做所有的完成工作

 从 post-oper callback例程中返回FLT_POSTOP_FINISHED_PROCESSING. 参见Writing Post-oper Callback Routines

 在安全的IRQL做完成工作

 从 post-oper callback例程中调用FltDoCompletionProcessingWhenSafe.

参见Ensuring that Completion Processing is Performed at Safe IRQL.

 从完成例程中Signal一个事件

 从此操作的post-oper callback例程中返回FLT_PREOP_SYNCHRONIZE. 

filter管理器在IRQL <= APC_LEVEL像调用pre-oper callback例程一样调用同一线程context中的post-oper callback例程.

Returning FLT_PREOP_SYNCHRONIZE.

 令一个成功的create操作失败

 createpost-oper callback例程中调用 FltCancelFileOpen. 

设置一个适当的错误NTSTATUS值到该操作的 FLT_CALLBACK_DATA 结构的成员IoStatus .

返回FLT_POSTOP_FINISHED_PROCESSING.

参见Failing an I/O Operation in a Post-oper Callback Routine.

 不接受某个I/O走fast I/O路线

 从该操作的post-oper callback例程中返回FLT_STATUS_DISALLOW_FAST_IO. 参见Disallowing a Fast I/O Operation in a Pre-oper Callback Routine.

 修改某个I/O操作的参数

 设置已修改的参数值到该操作的FLT_CALLBACK_DATA 结构的成员Iopb 

要通过调用FltSetCallbackDataDirty标记FLT_CALLBACK_DATA结构为dirty,除非你已经修改了FLT_CALLBACK_DATA结构的成员IoStatus中的内容.

参见Modifying the Parameters for an I/O Operation.

 锁住这个操作的user buffer

 使用Accessing the User Buffers for an I/O Operation一文中描述的技术和指导方针.  安装Minifilter驱动

 

 
2. 安装一个minifilter驱动


XP及更新的Windows OS中,你应该用INF文件和一个安装应用程序来安装你的minifilter驱动. (2K及更早的OS中,minifilter 驱动一般是借助Service Control Manager安装的.)

注意"基于INF的安装"仅仅意味着你需要使用一个INF文件来复制文件并存储信息到注册表中.你没有被要求用仅仅一个INF文件来安装你的整个产品,且你没有被要求为你的驱动提供一个"右键点击安装"的选项.


2.1 为minifilter驱动创建一个INF文件


一个文件系统minifilter驱动的INF文件一般包含以下部分:


2.1.1 Version (必需的)


版本部分指定由minifilter驱动的类型决定的一个类和GUID,如下例.

[Version]
Signature   = "$WINDOWS NT$"
Class       = "ActivityMonitor"
ClassGuid   = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider    = %Msft%
DriverVer   = 10/09/2001,1.0.0.0
CatalogFile =

下表是文件系统minifilter驱动应该在版本中指定的值.
Entry
Value
Signature
"$WINDOWS NT$"
Class
参考 File System Filter Driver Classes and Class GUIDs.
ClassGuid
参考 File System Filter Driver Classes and Class GUIDs.
Provider
在你自己的INF文件中,你应该指定一个provider 而非Microsoft.
DriverVer
参考 INF DriverVer directive.
CatalogFile
对反病毒minifilter驱动来说这是有符号的值,此入口包含一个WHQL提供的catalog文件的名.所有其他minifilter驱动应令此处为空白.详细信息,参考INF Version Section一文中对CatalogFile入口的描述.


2.1.2 DestinationDirs (可选但推荐)


DestinationDirs 指定minifilter驱动和应用程序文件将会被复制到哪个目录下.

在这个地方和ServiceInstall 中,你可以指定一个由系统定义的已知系统目录的数字值. 有关这些值的详情,参考 INF DestinationDirs Section.下例中,值12代表驱动目录(在基于NT的平台上是%windir%\system32\drivers),值10代表Windows目录(%windir%).

[DestinationDirs]
DefaultDestDir = 12
Minispy.DriverFiles = 12
Minispy.UserFiles   = 10,FltMgr


2.1.3 DefaultInstall (必需的)


在DefaultInstall 中,一个CopyFiles directive 会复制minifilter驱动的驱动文件和用户应用程序文件到DestinationDirs指定的目标中.

注意  CopyFiles directive不应涉及catalog文件或INF文件自身. SetupAPI会自动复制这些文件.

你可以创建一个单一的INF文件来在多版本的Windows OS上安装你的驱动.你可以通过为每个版本的OS创建额外的DefaultInstall、DefaultInstall.Services、DefaultUninstall和DefaultUninstall.Services来创建这种INF文件. 每一个都以decoration (例如.ntx86, .ntia64或.nt)修饰来指定它要应用的是哪个OS版本.更多关于创建此类INF文件的信息,参考 Creating INF Files for Multiple Platforms and Operating Systems.

下例是一个典型的DefaultInstall项.

[DefaultInstall]
OptionDesc = %MinispyServiceDesc%
CopyFiles = Minispy.DriverFiles, Minispy.UserFiles


2.1.4 DefaultInstall.Services (必需的)


DefaultInstall.Services包含一个AddService directive ,它控制驱动的服务如何以及什么时候被加载,如下例:

[DefaultInstall.Services]
AddService = %MinispyServiceName%,,Minispy.Service


2.1.5 ServiceInstall (必需的)


ServiceInstall 包含用于加载驱动服务的信息.在例子MiniSpy驱动中,此项的被命名为"Minispy.Service", 如下例所示.ServiceInstall 的名必须出现在DefaultInstall.Services 项的AddService directive 中.

[Minispy.Service]
DisplayName    = %MinispyServiceName%
Description    = %MinispyServiceDesc%
ServiceBinary  = %\minispy.sys
ServiceType    = 2 ;    SERVICE_FILE_SYSTEM_DRIVER
StartType      = 3 ;    SERVICE_DEMAND_START
ErrorControl   = 1 ;    SERVICE_ERROR_NORMAL%
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg         = Minispy.AddRegistry
Dependencies   = FltMgr

ServiceType入口指定了服务的类型.Minifilter驱动应指定一个为2的值(SERVICE_FILE_SYSTEM_DRIVER).更多关于ServiceType 入口的信息,参考 INF AddService Directive.

StartType 指定了什么时候启动服务.下表列出了它的取值范围及这些值的启动类型.

描述
0x00000000
SERVICE_BOOT_START
0x00000001
SERVICE_SYSTEM_START
0x00000002
SERVICE_AUTO_START
0x00000003
SERVICE_DEMAND_START
0x00000004
SERVICE_DISABLED

更多有关这些启动类型的信息,参考What Determines When a Driver Is Loaded一文中的"Driver Start Types".

LoadOrderGroup 提供filter管理器需要用来确保minifilter驱动和legacy FSFD之间的互用性的信息.你应该为你正在开发的minifilter驱动类型指定一个合适的LoadOrderGroup 值.详情参考 Load Order Groups and Altitudes for Minifilter Drivers.

注意你必须指定一个LoadOrderGroup 值,即使你的minifilter驱动的启动类型不是SERVICE_BOOT_START.在这里,minifilter驱动和legacy FSFD有些不同.

注意  filter管理器的StartType值为SERVICE_BOOT_START,它的LoadOrderGroup值为FSFilter Infrastructure. 这些值确保filter管理器总在一切minifilter驱动之前被加载.

更多关于驱动被加载时StartType和LoadOrderGroup入口如何决定,参考 What Determines When a Driver Is Loaded.

注意  对minifilter驱动来说,StartType和LoadOrderGroup值不决定minifilter驱动在minifilter实例栈中绑定到哪.这个位置是由minifilter实例的altitude决定的.

ErrorControl指定如果在系统启动期间服务失败了要进行的行为. Minifilter 驱动应指定它为1 (SERVICE_ERROR_NORMAL). 更多关于ErrorControl 的信息参考 INF AddService Directive.

AddReg directive 涉及一个或多个INF作者定义的AddRegistry 项,这个项包含要被存储到最新安装的服务的注册值中的信息.Minifilter驱动用AddRegistry 来定义minifilter驱动实例并指定一个默认的实例.

Dependencies 指定了驱动依靠的一切服务或加载顺序组的名字.一切minifilter驱动都必须指定FltMgr,它是filter管理器的服务名.


2.1.6 AddRegistry (必需的)


AddRegistry 添加key和值到注册表中. Minifilter驱动用AddRegistry 项来定义minifilter实例并指定一个默认实例.只要filter管理器为该minifilter驱动创建一个新的实例这个信息就会被用到.

在例子MiniSpy 驱动中,以下AddRegistry 和Strings 中的%strkey% 记号定义三个实例,每一个都命名为MiniSpy驱动的默认实例.

[Minispy.AddRegistry]
HKR,%RegInstancesSubkeyName%,%RegDefaultInstanceValueName%,0x00000000,�faultInstance%
HKR,%RegInstancesSubkeyName%""%Instance1.Name%,%RegAltitudeValueName%,0x00000000,%Instance1.Altitude%
HKR,%RegInstancesSubkeyName%""%Instance1.Name%,%RegFlagsValueName%,0x00010001,%Instance1.Flags%
HKR,%RegInstancesSubkeyName%""%Instance2.Name%,%RegAltitudeValueName%,0x00000000,%Instance2.Altitude%
HKR,%RegInstancesSubkeyName%""%Instance2.Name%,%RegFlagsValueName%,0x00010001,%Instance2.Flags%
HKR,%RegInstancesSubkeyName%""%Instance3.Name%,%RegAltitudeValueName%,0x00000000,%Instance3.Altitude%
HKR,%RegInstancesSubkeyName%""%Instance3.Name%,%RegFlagsValueName%,0x00010001,%Instance3.Flags%


2.1.7 DefaultUninstall (可选)


DefaultUninstall 可选但如果你的驱动可以被卸载的话则推荐你使用它.它包含 DelFiles和DelReg directive来移除文件和注册值入口,如下列所示.

[DefaultUninstall]
DelFiles   = Minispy.DriverFiles, Minispy.UserFiles
DelReg     = Minispy.DelRegistry


2.1.8 DefaultUninstall.Services (可选)


DefaultUninstall.Services 可选但如果你的驱动可以被卸载的话则推荐你使用它.它包含DelService directive来移除minifilter驱动的服务,如下来自MiniSpy驱动的例子所示.

注意  DelService directive应总是指定SPSVCINST_STOPSERVICE标记(0x00000200)来在服务被删除之前停止它.

[DefaultUninstall.Services]
DelService = Minispy,0x200


2.1.9 Strings (必需的)


Strings 定义INF文件中使用的每一个%strkey%记号.

你可以通过在INF文件中创建额外的现场指定的Strings.LanguageID 项来创建一个单一的国际化INF文件.

下例是一个典型的Strings项.

[Strings]
Msft               = "Microsoft Corporation"
MinispyServiceDesc = "Minispy mini-filter driver"
MinispyServiceName = "Minispy"
RegInstancesSubkeyName = "Instances"
RegDefaultInstanceValueName  = "DefaultInstance"
RegAltitudeValueName    = "Altitude"
RegFlagsValueName  = "Flags"

DefaultInstance    = "Minispy - Top Instance"
Instance1.Name     = "Minispy - Middle Instance"
Instance1.Altitude = "370000"
Instance1.Flags    = 0x1 ; Suppress automatic attachments
Instance2.Name     = "Minispy - Bottom Instance"
Instance2.Altitude = "365000"
Instance2.Flags    = 0x1 ; Suppress automatic attachments
Instance3.Name     = "Minispy - Top Instance"
Instance3.Altitude = "385000"
Instance3.Flags    = 0x1 ; Suppress automatic attachments

 

注意  从x64-based Windows Vista systems开始,所有的内核模式组件,包括非PnP(即插即用)驱动,比如FSD(文件系统, legacy filter和minifilter驱动), 都必须被签名以便加载和执行. 因为这个情况,以下是FSD相关的信息:

非PnP驱动的INF文件,包括FSD,不要求包含[Manufacturer] 或[Models]项.

SignTool 命令行工具, 位于WDK安装目录的\bin\SelfSign 目录中,可以直接用于"嵌入式的签名"一个驱动SYS 执行文件.由于性能原因,引导启动的驱动必须包含一个内含的签名.

给定一个INF文件, Inf2cat 命令行工具可用于创建一个驱动包的catalog (.cat)文件.仅catalog文件可以接收WHQL 标识签名.

在管理员特权下,一个未签名的驱动也可以在以Vista开始的x64-based系统上安装.不过,这个驱动的加载会失败因为它是未签名的.

关于驱动签名的详细信息,参考Driver Signing.

掌握签名处理的详细信息, 参考Kernel-Mode Code Signing Walkthrough.

所有的内核模式组件,包括自定义的内核模式开发工具,必须被签名.更多信息参考Signing Software for Use in a Driver Development Environment (Windows Vista).

2.2 Minifilter驱动的加载顺序和Altitudes

Microsoft Windows XP, Windows 2000 SP4 with Update Rollup 1, 和较新的OS专门为在系统启动时加载的FSFD和minifilter驱动提供了一套加载顺序组.

Legacy FSFD可以只绑定到一个现有FSD 栈的顶部,但不可以绑定到某个栈的中间.其结果是,驱动的启动类型和加载顺序组对legacy FSFD来说很重要,因为一个过滤驱动越早加载,它就可以绑定到FSD栈上越低的位置.

驱动的加载首先基于它的表示系统引导阶段的启动类型.更多关于启动类型的信息参考What Determines When a Driver Is Loaded一文中的"Driver Start Types". 所有启动类型为SERVICE_BOOT_START的FSFD和minifilter驱动都将会在启动类型为SERVICE_SYSTEM_START或 SERVICE_AUTO_START的驱动之前加载.启动类型由用于安装minifilter驱动的INF文件的ServiceInstall 项中的StartType入口指定.在每一种启动类型类别中,加载顺序组决定了FSFD和minifilter驱动什么时候会被加载.

一个minifilter驱动可以在任何时间加载.minifilter驱动仍然需要加载顺序组的概念来与legacy FSFD协同工作.每一个minifilter驱动必须有一个叫做altitude的唯一标识符.一个minifilter驱动的altitude定义了它加载时在I/O栈中相对其他minifilter驱动的位置. Altitude是一个无限精密的串就像小数一样.altitude数字低的minifilter驱动加载到I/O栈中的位置要低于一个altitude数字高的minifilter驱动.

每一个加载顺序组都有一个定义好的altitude范围.minifilter驱动的altitude的分配由微软管理.要想为你的minifilter驱动请求一个altitude,去 WHDC Minifilter Altitude Allocation 网址看看.

Minifilter驱动必须从表示某个加载顺序组的一个altitude范围中指定一个altitude值.minifilter驱动的altitude值在用于安装这个minifilter驱动的INF文件的Strings 项的实例定义中指定.实例定义也可以在对InstanceSetupCallback例程的调用中的FLT_REGISTRATION结构里指定.一个minifilter驱动可以有多个实例和altitude.这些实例定义的应用跨越所有的卷.

以下启动类型和加载顺序组的规则决定了一个minifilter驱动什么时候会被加载:

启动类型相同且在同一加载顺序组中的minifilter驱动和其他FSFD会在同一时间被加载.

在每一个加载顺序组中,FSFD和minifilter驱动一般都以随机顺序加载.这就导致了驱动的加载基于驱动的安装顺序.

如果某个FSFD或minifilter驱动没有指定一个加载顺序组,那它就会在拥有相同启动类型(就是指定了加载顺序组)的所有其他驱动加载之后加载.

下表是系统定义的minifilter驱动的加载顺序组和altitude范围.对每一个加载顺序组来说,加载顺序组栏包含了应该被指定给minifilter的INF文件ServiceInstall 项LoadOrderGroup入口中的组的值. Altitude范围栏包含一个特定的加载顺序组的altitude范围.一个minifilter驱动必须请求微软在适当的加载顺序组或组中分配的一个altitude.

注意加载顺序组和Altitude范围的罗列顺序与它们出现在栈中的顺序相同而于它们的加载顺序相反.

          

加载顺序组:Filter        

Altitude范围:420000-429999
    描述:此组与WIN2K及更早的OS中的Filter 加载顺序组一样. 它最后加载,因此绑定位置离文件系统最远.
FSFilter Top
400000-409999
此组是为必须绑定在所有其他FSFilter类型的驱动之上的过滤驱动准备的.


FSFilter Activity Monitor
360000-389999
此组包括观测和报告文件I/O的过滤驱动,比如FileSpy 和SFilter驱动例子.


FSFilter Undelete
340000-349999
此组包括恢复已删除的文件的过滤器


FSFilter Anti-Virus
320000-329999
此组包括在文件I/O期间探测并杀毒的过滤驱动


FSFilter Replication
300000-309999
此组包括复制文件数据给远程服务器的过滤驱动


FSFilter Continuous Backup
280000-289999
此组包括复制文件数据给backup媒体的过滤驱动


FSFilter Content Screener
260000-269999
此组包括阻止指定文件或文件内容的创建的过滤驱动


FSFilter Quota Management
240000-249999
此组包括提供增强的文件系统quota的过滤驱动


FSFilter System Recovery
220000-229999
此组包括执行操作来维护OS完整性比如System Restore (SR) filter的过滤驱动


FSFilter Cluster File System
200000-209999
此组包括用于那些跨网络提供文件服务器元数据的的产品的过滤驱动


FSFilter HSM
180000-189999
此组包括执行分层的存储器管理的过滤驱动


FSFilter Imaging
170000-175000
此组包括像ZIP一样提供一个虚拟名字空间的过滤驱动
此加载组在Vista及以后版本的OS上可用


FSFilter Compression
160000-169999
此组包括执行文件数据压缩的过滤驱动


FSFilter Encryption
140000-149999
此组包括在文件I/O期间加密和解密数据的过滤驱动


FSFilter Virtualization
130000- 139999
此组包括虚拟化文件路径比如Vista中添加的Least Authorized User (LUA) 过滤驱动这样的过滤驱动
此加载组在Vista及以后版本的OS上可用


FSFilter Physical Quota Management
120000-129999
此组包括通过用物理块计数管理quota的过滤驱动


FSFilter Open File
100000-109999
此组包括提供已经打开的文件的快照的过滤驱动


FSFilter Security Enhancer
80000-89999
此组包括申请lockdown并增强访问控制列表(ACLs)的过滤驱动


FSFilter Copy Protection
60000-69999
此组包括做媒体上数据是否超出边界的检查的过滤驱动


FSFilter Bottom
40000-49999
此组是为必须绑定在所有其他FSFilter类型的驱动之下的过滤驱动准备的


FSFilter System
20000-29999
保留为内部使用


FSFilter Infrastructure

保留为内部使用. 此组首先加载,因此绑定位置最接近文件系统


3. 写Minifilter驱动的DriverEntry例程


每一个文件系统minifilter驱动都必须有一个DriverEntry 例程.当minifilter驱动被加载时该例程会被调用.

DriverEntry例程执行全局初始化,注册minifilter驱动并初始化过滤.此例程在一个系统线程上下文中且在IRQL PASSIVE_LEVEL运行.

它的定义如下:

NTSTATUS
(*PDRIVER_INITIALIZE) (
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );

DriverEntry有两个入参.首先是DriverObject,它是驱动对象在minifilter驱动加载时被创建.第二个是RegistryPath, 它是包含minifilter驱动的注册key的一个路径的Unicode串.

Minifilter驱动的DriverEntry 例程必须按序执行以下步骤:

1. 执行minifilter驱动的一切所需的全局初始化.
2. 调用FltRegisterFilter注册minifilter驱动.
3. 调用FltStartFiltering发起过滤..
4. 返回一个适当的NTSTATUS 值.


3.1 注册Minifilter驱动


每一个minifilter驱动都必须从它的DriverEntry例程中调用FltRegisterFilter来添加它自己到已注册的minifilter驱动的全局列表中并向filter管理器提供一套callback例程和其他驱动相关的信息.

在MiniSpy例子中,minifilter驱动的注册过程如下:

NTSTATUS status;
status = FltRegisterFilter(
           DriverObject,                  //Driver
           &FilterRegistration,           //Registration
           &MiniSpyData.FilterHandle);    //RetFilter

FltRegisterFilter有两个入参.第一个Driver, 是minifilter驱动在它的DriverEntry例程的入参DriverObject中收到的驱动对象的指针. 其次Registration, 是包含minifilter驱动callback例程的入口点的FLT_REGISTRATION结构的一个指针.

另外, FltRegisterFilter 有一个出参RetFilter,它接收minifilter驱动的一个不透明的过滤器指针.这个过滤器指针是许多FltXxx支持例程的一个必需的入参,包括FltStartFiltering和FltUnregisterFilter.


3.2 发起过滤


在调用FltRegisterFilter后,minifilter驱动的DriverEntry例程一般会调用FltStartFiltering 来开始过滤I/O操作.

每一个minifilter驱动都必须从它的DriverEntry例程中调用FltStartFiltering来通过filter管理器,该minifilter驱动已经准备好开始绑定到卷上并开始过滤I/O请求.在minifilter驱动调用了FltStartFiltering之后, filter管理器会把它当做一个完全活动的minifilter驱动,把I/O请求和要绑定的卷的通知提供给它. Minifilter驱动甚至要在FltStartFiltering返回之前就必须准备好开始接收这些I/O请求和通知.

在MiniSpy驱动例子中, FltStartFiltering 如下被调用:

status = FltStartFiltering( MiniSpyData.FilterHandle );
if( !NT_SUCCESS( status )) {
  FltUnregisterFilter( MiniSpyData.FilterHandle );
}

如果对FltStartFiltering 的调用不返回STATUS_SUCCESS, minifilter驱动必须调用FltUnregisterFilter 来注销它自己.

 

3.3 从一个minifilter DriverEntry例程中返回状态


一个FSFD的DriverEntry 例程通常返回STATUS_SUCCESS.不过,如果驱动初始化失败了,DriverEntry 例程应该返回一个适当的错误状态值.

如果DriverEntry例程返回一个非正确的状态值,系统会通过卸载该驱动来做出响应.由于这个原因, DriverEntry 例程必须总是在返回一个非正确的状态值之前free一切为像设备对象这样的系统资源分配的内存.

4. 写Minifilter驱动的FilterUnloadCallback例程

minifilter驱动可随意地注册一个 PFLT_FILTER_UNLOAD_CALLBACK类型的例程作为它的FilterUnloadCallback 例程.这个callback例程也可以说是minifilter驱动的卸载例程.

Minifilter驱动不一定要注册一个FilterUnloadCallback 例程.不过,我们强烈地推荐要注册这个callback例程,因为如果一个minifilter驱动不注册一个FilterUnloadCallback 例程,那么这个驱动就不可以被卸载.

要注册这个callback例程, minifilter驱动得在FLT_REGISTRATION结构的成员FilterUnloadCallback中存一个PFLT_FILTER_UNLOAD_CALLBACK类型的例程的地址,该结构会被minifilter驱动作为它的DriverEntry例程中的 FltRegisterFilter的一个参数.

4.1FilterUnloadCallback例程什么时候会被调用

Filter管理器会在以下列方式之一卸载minifilter驱动之前调用该驱动的 FilterUnloadCallback 例程:

非强制卸载.当某个用户模式应用程序已经调用了FilterUnload或某个内核模式驱动已经调用了FltUnloadFilter时会发生这种类型的卸载. 当你在命令提示符里打fltmc unload时这种类型的卸载也会发生.

强制卸载. 当你通过在命令提示符里打sc stop或net stop来发出一个服务停止请求时会发生这种类型的卸载. (更多关于sc stop和net stop 命令的信息,点击开始菜单中的Help and Support .) 当某个用户模式应用程序调用了微软Win32 ControlService 函数,在参数dwControl中传SERVICE_CONTROL_STOP控制代码时它也会发生. (更多关于Win32 服务函数的信息,参考Microsoft Windows SDK documentation.)

对一个非强制卸载来说,如果minifilter驱动的FilterUnloadCallback 例程返回了一个错误或警告的NTSTATUS 值如STATUS_FLT_DO_NOT_DETACH, filter管理器就不会卸载该驱动.

对一个强制卸载来说,filter管理器在minifilter驱动的FilterUnloadCallback例程被调用之后,即使该例程返回了一个错误或警告NTSTATUS值比如STATUS_FLT_DO_NOT_DETACH这个minifilter驱动也会被卸载.

要禁用某个minifilter驱动的强制卸载,该驱动得在FLT_REGISTRATION结构的成员Flags中设置FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP标记,该结构会被minifilter驱动作为它的DriverEntry例程中的 FltRegisterFilter的一个参数.当设置了这个标记时,filter管理器通常执行非强制卸载请求.不过,强制卸载请求会失败.filter管理器为失败的卸载请求调用minifilter驱动的FilterUnloadCallback 例程.

注意如果某个minifilter驱动的DriverEntry 例程返回了一个警告或错误的 NTSTATUS 值,那么FilterUnloadCallback 例程就不会被调用;filter管理器会简单地卸载这个minifilter驱动.

FilterUnloadCallback 不会在系统关闭期间被调用.一个必须执行关闭处理的minifilter驱动应为IRP_MJ_SHUTDOWN操作注册一个pre-oper callback 例程.


4.2 写一个FilterUnloadCallback例程


FilterUnloadCallback 例程的定义如下:

typedef NTSTATUS
(*PFLT_FILTER_UNLOAD_CALLBACK) (
    FLT_FILTER_UNLOAD_FLAGS Flags
    );

FilterUnloadCallback 有一个入参Flags, 它可以是NULL或者 FLTFL_FILTER_UNLOAD_MANDATORY. Filter管理器设置这个参数为FLTFL_FILTER_UNLOAD_MANDATORY来指示卸载操作是强制的. 更多此参数的信息,参考PFLT_FILTER_UNLOAD_CALLBACK.

Minifilter驱动的FilterUnloadCallback 例程必须执行以下步骤:

关闭一切内核模式通信服务器端口句柄.

调用FltUnregisterFilter 来注册这个minifilter驱动.

执行一切所需的全局cleanup.

返回一个合适的NTSTATUS值.

 

4.2.1 关闭通信服务器端口


如果minifilter驱动之前调用FltCreateCommunicationPort打开了一个内核模式通信服务器端口,则它必须调用FltCloseCommunicationPort关闭这个端口. 为防止系统在卸载处理期间挂起,minifilter驱动的FilterUnloadCallback 例程必须在调用FltUnregisterFilter之前关闭这个端口.

如果某个用户模式应用程序对该通信服务器端口有了一个打开的连接,在FltCloseCommunicationPort返回之后该连接的一切客户端口仍被保持打开状态.不过, filter管理器会在minifilter驱动被卸载时关闭一切客户端口.


4.2.2 注销Minifilter


Minifilter驱动的FilterUnloadCallback 例程必须调用FltUnregisterFilter 来注销这个驱动.调用FltUnregisterFilter 会引起下列事情的发生:

Minifilter驱动的callback例程被注销.

Minifilter驱动的实例被拆卸,它的InstanceTeardownStartCallback 和InstanceTeardownCompleteCallback 例程都会为每一个minifilter驱动实例被调用.

如果minifilter驱动设置了卷、实例、流或流句柄上的一切上下文,这些上下文都会被删除.如果该驱动已经为某个给定的上下文类型注册了一个CleanupContext callback 例程,那么filter管理器就会在删除该上下文之前调用CleanupContext 例程.

如果minifilter驱动的不透明的过滤器指针上还有未决rundown引用, 那么FltUnregisterFilter就会进入一个等待状态直到它们被移除.未决rundown引用的发生常常是因为minifilter驱动已经调用了FltQueueGenericWorkItem来插入一个工作项到一个系统工作队列中, 且该工作项还没有被出列和处理. (filter管理器会在minifilter驱动调用FltQueueGenericWorkItem时添加rundown引用而在minifilter驱动的工作例程返回时移除该引用.)

如果minifilter驱动已经调用了任何会添加一个到其不透明的过滤器指针上的未决rundown引用的例程,比如FltObjectReference和FltGetFilterFromInstance,但后来没有调用FltObjectDereference,也会发生未决rundown引用.


4.2.3 执行全局Cleanup


Minifilter驱动的FilterUnloadCallback 例程必须执行一切所需的全局cleanup.以下是一个minifilter驱动可能执行的全局cleanup任务:

调用ExDeleteResourceLite 来删除一个被ExInitializeResourceLite 初始化了的全局资源变量.

调用ExFreePool或ExFreePoolWithTag来free被ExAllocatePoolWithTag 这样的例程分配的全局内存.

分别调用ExDeleteNPagedLookasideList或ExDeletePagedLookasideList来删除由ExInitializeNPagedLookasideList或ExInitializePagedLookasideList分配的一个lookaside列表.

调用PsRemoveCreateThreadNotifyRoutine或PsRemoveLoadImageNotifyRoutine来分别注销由PsSetCreateThreadNotifyRoutine 或PsSetLoadImageNotifyRoutine注册的一个全局callback例程.

4.2.4 从一个FilterUnloadCallback例程中返回状态


Minifilter驱动的FilterUnloadCallback 例程通常返回STATUS_SUCCESS.

要拒绝一个非强制的卸载操作,minifilter驱动应返回一个合适的警告或错误NTSTATUS值比如STATUS_FLT_DO_NOT_DETACH.强制卸载操作的详情参考写FilterUnloadCallback例程和FLT_FILTER_UNLOAD_CALLBACK.

如果FilterUnloadCallback 例程返回一个警告或错误的NTSTATUS值且卸载操作是非强制的,minifilter驱动不会被卸载.


5. 写Callback例程的Pre-oper和Post-oper


一个minifilter驱动可以在它的DriverEntry例程中为它需要过滤的任何类型的I/O操作注册至多一个pre-oper callback例程和至多一个post-oper callback例程. 

Minifilter可以选择要过滤哪种类型的I/O. minifilter驱动可以为一个给定类型的I/O操作只注册一个pre-oper callback 例程,也可只注册一个post-oper callback例程. Minifilter驱动只会收到那些它已经为之注册了一个pre-oper或post-oper callback例程的I/O操作.

pre-oper callback例程和legacy过滤驱动模型中的分发例程类似.当filter管理器处理一个I/O操作时,它会调用已经为这种类型的I/O操作注册过的minifilter驱动实例栈中的每个minifilter驱动的pre-oper callback例程. 栈中顶端的minifilter驱动—就是其实例拥有最高altitude的那个—会首先收到这个操作. 当该 minifilter驱动结束了对这个操作的处理时,它会把此操作返回给filter管理器,然后filter管理器会把操作传给下一个最高的(除了刚刚处理过这个操作的那个之外最高的)minifilter驱动,以此类推. 当 minifilter驱动实例栈中的所有minifilter驱动已经处理过了这个I/O操作—除非某个minifilter驱动已经完成了这个I/O操作---filter管理器发送此操作给legacy过滤器和文件系统.
post-oper callback例程与legacy过滤驱动模型中的完成例程类似.当I/O管理器把操作传给已经为某个I/O操作注册了完成例程的文件系统和legacy过滤器时,该操作的完成处理就开始了. 在这些完成例程结束之后,filter管理器会执行对此操作的完成处理. 接着filter管理器会调用已经为这种类型的I/O操作注册过的minifilter驱动实例栈中的每个minifilter驱动的post-oper callback例程.栈中的底部minifilter驱动—就是其实例拥有最低 altitude的那个—会首先收到这个操作.当该minifilter驱动结束对这个操作的处理时,它把操作返回给filter管理器,然后filter管理器会把操作传给下一个最低的minifilter驱动,以此类推.


5.1 注册Pre-oper和Post-oper Callback例程


要注册pre-oper callback例程和 post-oper callback例程,minifilter驱动只需在其DriverEntry例程中调用FltRegisterFilter.minifilter驱动要把一个FLT_REGISTRATION结构的指针传给该函数的 Registration参数. 这个结构的成员OperationRegistration包含一组FLT_OPERATION_REGISTRATION 结构的指针, 其中之一是minifilter驱动必须过滤的.

此指针组中的每一个FLT_OPERATION_REGISTRATION结构,除了最后一个, 都包含以下信息:

操作的主函数代码

对read和write操作(IRP_MJ_READ和IRP_MJ_WRITE)来说,有一套用于基于IRP的I/O操作的标记,它们指定了是否要忽视cached I/O或分页I/O或两者都忽视

至多一个pre-oper callback例程和一个post-oper callback例程的入口点

此指针组的最后一个元素必须是{IRP_MJ_OPERATION_END}.

以下从Scanner minifilter驱动中节选出来的代码展示了一组 FLT_OPERATION_REGISTRATION结构.Scanner minifilter驱动为IRP_MJ_CREATE注册了pre-oper和post-oper callback例程,为IRP_MJ_CLEANUP和IRP_MJ_WRITE例子注册了pre-oper callback例程.

const FLT_OPERATION_REGISTRATION Callbacks[] = {
    {IRP_MJ_CREATE,
     0,
     ScannerPreCreate,
     ScannerPostCreate},
    {IRP_MJ_CLEANUP,
     0,
     ScannerPreCleanup,
     NULL},
    {IRP_MJ_WRITE,
     0,
     ScannerPreWrite,
     NULL},
    {IRP_MJ_OPERATION_END}
};


5.2 在Minifilter驱动中过滤I/O操作


以下是几个在文件系统minifilter驱动中过滤指定类型的I/O操作的几个指导方针:

IRP_MJ_CREATE的pre-oper callback例程不能为文件、流或流句柄查询或设置context,因为在pre-create时,计划要创建的文件或流还没有被确定.

IRP_MJ_CLOSE的post-oper callback 例程不能为文件、流或流句柄查询context,因为相关的系统内部结构都在调用post-close例程之前被释放了.

Minifilter驱动一定不能令IRP_MJ_CLEANUP 或IRP_MJ_CLOSE 操作失败. 这些操作可以被pend,被返回给filter管理器或以STATUS_SUCCESS被完成. 不过,一个pre-oper callback例程一定不能令这些操作失败.

Minifilter驱动不可以为IRP_MJ_SHUTDOWN 注册一个post-oper callback例程.

5.3 写Pre-oper Callback例程


一个文件系统minifilter驱动用一个或多个pre-oper callback例程来过滤I/O操作. Pre-oper callback例程 与legacy FSFD中使用的分发例程类似.

Minifilter驱动通过存储callback例程的入口点到FLT_REGISTRATION结构的成员OperationRegistration 中来为某个特定类型的I/O操作注册一个pre-oper callback例程,此结构会被minifilter驱动在其DriverEntry例程中作为一个参数传给 FltRegisterFilter.

Minifilter驱动仅接收那些它们已经为之注册了一个pre-oper或post-oper callback例程的I/O操作.minifilter驱动可以只注册一个pre-oper callback例程或只注册一个post-oper callback例程.

pre-oper callback例程的定义如下:

typedef FLT_PREOP_CALLBACK_STATUS
(*PFLT_PRE_OPERATION_CALLBACK) (
    IN OUT PFLT_CALLBACK_DATA Data,
    IN PCFLT_RELATED_OBJECTS FltObjects,
    OUT PVOID *CompletionContext
    );

像一个分发例程一样,一个pre-oper callback例程可以在IRQL = PASSIVE_LEVEL 或在IRQL = APC_LEVEL被调用.一般它在发起I/O请求的线程的context中在IRQL = PASSIVE_LEVEL被调用.对fast I/O和FsFilter操作来说,pre-oper callback例程总在IRQL = PASSIVE_LEVEL被调用.不过,对一个基于IRP的操作来说,如果一个更高层的过滤器或minifilter驱动pend此操作来让用worker线程做处理的话,minifilter驱动的pre-oper callback例程可以在一个系统worker线程的context中被调用.

当filter管理器为一个给定的I/O操作调用某个minifilter驱动的pre-oper callback例程时,该minifilter驱动临时地控制此I/O操作.minifilter驱动在它做完以下事情之前会保持其控制权:

从pre-oper callback 例程中返回一个除了FLT_PREOP_PENDING之外的状态值.

从一个已经处理了在pre-oper callback例程中被pend的操作的工作例程中调用FltCompletePendedPre-oper.

5.3.1 在minifilter实例栈上向下传I/O操作


 

当某个minifilter驱动的pre-oper callback例程或工作例程返回一个I/O操作给filter管理器时, filter管理器把该操作发送给minifilter驱动实例栈中在当前minifilter驱动之下的minifilter驱动,或给legacy过滤器和文件系统来做进一步处理.

pre-oper callback例程通过返回下列状态值之一来返回I/O操作给filter管理器进行深入处理:

FLT_PREOP_SUCCESS_NO_CALLBACK (所有操作类型)

FLT_PREOP_SUCCESS_WITH_CALLBACK (所有操作类型)

FLT_PREOP_SYNCHRONIZE (仅基于IRP的I/O)

注意  尽管FLT_PREOP_SYNCHRONIZE只针对基于IRP的I/O操作,你也可以为其他操作类型返回这个值.如果是为了非基于IRP的I/O操作返回此值,则filter管理器会把它当做FLT_PREOP_SUCCESS_WITH_CALLBACK来处理.

在pre-oper callback例程中被pend的操作的工作例程,当它调用FltCompletePendedPre-oper来恢复对被pend的I/O的处理时filter管理器通过在CallbackStatus参数中传递上述状态值之一返回I/O操作给filter管理器.


5.3.1.1 返回 FLT_PREOP_SUCCESS_WITH_CALLBACK


如果某个minifilter驱动的pre-oper callback例程返回了FLT_PREOP_SUCCESS_WITH_CALLBACK, filter管理器就会在I/O完成期间调用它的post-oper callback例程.

注意  如果minifilter驱动的pre-oper callback例程返回了 FLT_PREOP_SUCCESS_WITH_CALLBACK但该驱动还没有为操作注册一个post-oper callback例程,那么系统会在checked build上assert.

如果这个minifilter驱动的pre-oper callback例程返回了 FLT_PREOP_SUCCESS_WITH_CALLBACK, 那它就可以在其输出参数CompletionContext中返回一个非NULL的值. 该参数是一个可选的context指针,它被传给相应的post-oper callback例程.此例程会在其 CompletionContext 入参中收到这个指针.

对所有类型的I/O操作都可以返回FLT_PREOP_SUCCESS_WITH_CALLBACK状态值.


5.3.1.2 返回FLT_PREOP_SUCCESS_NO_CALLBACK


如果某个minifilter驱动的pre-oper callback例程 返回了 FLT_PREOP_SUCCESS_NO_CALLBACK, 那么在I/O操作期间filter管理器就不能调用它的 post-oper callback例程。

如果这个minifilter驱动的pre-oper callback例程返回了FLT_PREOP_SUCCESS_NO_CALLBACK, 那么它必须在它的出参CompletionContext 返回NULL.

对所有类型的I/O操作都可以返回FLT_PREOP_SUCCESS_NO_CALLBACK状态值.


5.3.1.3 返回FLT_PREOP_SYNCHRONIZE


如果某个minifilter驱动的pre-oper callback例程 通过返回FLT_PREOP_SYNCHRONIZE 来同步一个I/O操作,那么filter管理器就会在I/O完成期间调用它的post-oper callback例程.

Filter管理器像调用pre-oper callback一样在同一线程的上下文中在 IRQL <= APC_LEVEL调用这个minifilter驱动的post-oper callback例程. (注意这个线程上下文不一定是发起线程上下文.)

注意  如果minifilter驱动的pre-oper callback例程返回了 FLT_PREOP_SYNCHRONIZE, 但它还没有为操作注册一个 post-oper callback例程,系统会在checked build上assert.

若minifilter驱动pre-oper callback例程返回FLT_PREOP_SYNCHRONIZE, 那么它就可以在其出参CompletionContext中返回一个非NULL值. 此参数是一个可选的context指针,它被传给相应的post-oper callback例程.该例程会在其入参CompletionContext 中收到这个指针.

Minifilter驱动的pre-oper callback例程应只对IRP-I/O操作返回FLT_PREOP_SYNCHRONIZE.不过,此状态值可以作为其他操作类型的返回值. 如果它作为返回值是不是IRP-I/O操作,filter管理器会把这个值当FLT_PREOP_SUCCESS_WITH_CALLBACK来处理.

用FLT_IS_IRP_OPERATION宏可以确定一个操作是不是一个IRP-I/O操作.

Minifilter驱动不应为create操作返回FLT_PREOP_SYNCHRONIZE, 因为这些操作已经被filter管理器同步了. 如果一个minifilter驱动已经注册了 IRP_MJ_CREATE 操作的pre-oper和post-oper例程,则这两个例程都会在IRQL = PASSIVE_LEVEL在同一线程上下文中被调用.

Minifilter不允许为异步读写操作返回FLT_PREOP_SYNCHRONIZE. 这么做严重地降低minifilter驱动和系统的性能,甚至引起死锁,例如,已修改的页writer thread会被阻塞.在为IRP的读写返回FLT_PREOP_SYNCHRONIZE之前,minifilter驱动应调用FltIsOperationSynchronous来校验操作是否同步.

以下类型的I/O操作不能被同步:

Oplock文件系统控制(FSCTL)操作(MajorFunction为 IRP_MJ_FILE_SYSTEM_CONTROL; FsControlCode为 FSCTL_REQUEST_FILTER_OPLOCK, FSCTL_REQUEST_BATCH_OPLOCK, FSCTL_REQUEST_OPLOCK_LEVEL_1, 或FSCTL_REQUEST_OPLOCK_LEVEL_2.)

通知改变目录操作(MajorFunction为IRP_MJ_DIRECTORY_CONTROL; MinorFunction为 IRP_MN_NOTIFY_CHANGE_DIRECTORY.)

Byte-range锁请求(MajorFunction为IRP_MJ_LOCK_CONTROL; MinorFunction为IRP_MN_LOCK.)

FLT_PREOP_SYNCHRONIZE不能作为以上任何操作的返回值。

5.3.2 在Pre-oper Callback例程中完成一个I/O操作


要完成一个I/O操作意味着你得停止该操作的处理,给它指定一个最终的NTSTATUS值并把它返回给filter manager.

当某个minifilter驱动完成了一个I/O操作时,filter管理器做以下这些事情:

不再把此操作发送给当前minifilter驱动之下的minifilter驱动、legacy过滤器以及文件系统.

调用minifilter驱动实例栈中在当前minifilter驱动之上的minifilter驱动的post-oper callback例程.

不再调用当前minifilter驱动对此操作的post-oper callback例程(如果存在一个的话).

Minifilter驱动的pre-oper callback例程通过执行以下这些步骤完成一个I/O操作:

1. 设置callback数据结构的IoStatus.Status域为此操作最终的NTSTATUS值.
2. 返回FLT_PREOP_COMPLETE.

一个完成一个I/O操作的pre-oper callback例程不可以设置一个非NULL的完成context (在出参CompletionContext 中).

Minifilter驱动也可以在一个之前被pend的I/O操作的工作例程中完成一个操作,它要执行的步骤如下:

1. 设置callback数据结构的IoStatus.Status域为此操作最终的NTSTATUS值.
2. 当工作例程调用FltCompletePendedPost-oper时,在参数CallbackStatus中传递FLT_PREOP_COMPLETE.

当完成一个I/O操作时,minifilter驱动必须设置callback数据结构的 IoStatus.Status域为此操作最终的NTSTATUS值,但是这个NTSTATUS值不可以是STATUS_PENDING或STATUS_FLT_DISALLOW_FAST_IO.对于一个 cleanup或close操作来说,这个域必须是STATUS_SUCCESS.这些操作不能以其他任何NTSTATUS值完成.

完成一个I/O操作常常牵涉依赖于NTSTATUS值令该操作成功或失败的操作:

要令I/O操作成功意味着以一个成功或如STATUS_SUCCESS值这样的信息化的NTSTATUS值完成它.

要令I/O操作失败意味着以一个错误或像STATUS_INVALID_DEVICE_REQUEST或者 STATUS_BUFFER_OVERFLOW这样的警告NTSTATUS值完成它.

NTSTATUS值的定义在ntstatus.h中.这些值分为四类:成功,信息化的, 警告和错误. 更多这些值的信息可以参考Using NTSTATUS Values一文.


5.3.3 在Pre-oper Callback例程中拒绝一个Fast I/O操作


在某些情况下,minifilter驱动可能会选择拒绝一个fast I/O操作而非完成它.这会阻止该操作使用fast I/O.

和完成I/O操作一样,拒绝fast I/O操作意味着停止在它上的处理并把它返回给filter管理器。不过,拒绝和完成不同。如果某个minifilter驱动拒绝一个由I/O管理器发出的fast I/O,I/O管理器可能会以等价的基于IRP的操作重发同一操作。

当某个minifilter驱动的pre-oper callback例程拒绝了一个fast I/O操作时,filter管理器会做以下这些:

不再发送操作给在当前minifilter驱动之下的minifilter驱动、legacy过滤器以及文件系统。

调用minifilter驱动实例栈中处于当前minifilter驱动之上的minifilter驱动的post-oper callback例程.

不再带哦用当前minifilter驱动用于此操作的post-oper callback例程,如果有一个的话.

Minifilter驱动通过从操作的pre-oper callback例程中返回FLT_PREOP_DISALLOW_FASTIO来拒绝一个fast I/O操作.

Pre-oper callback例程不应设置callback数据结构的IoStatus.Status域,因为filter管理器会自动设置此域为STATUS_FLT_DISALLOW_FAST_IO.

FLT_PREOP_DISALLOW_FASTIO仅可作为fast I/O操作的返回值.要决定一个操作是否为一个fast I/O操作,参考FLT_IS_FASTIO_OPERATION.

Minifilter驱动不可为IRP_MJ_SHUTDOWN,IRP_MJ_VOLUME_MOUNT或IRP_MJ_VOLUME_DISMOUNT操作返回FLT_PREOP_DISALLOW_FASTIO.


5.3.4 在Pre-oper Callback例程中Pend一个I/O操作


Minifilter驱动的pre-oper callback例程可以通过把操作发到一个系统工作队列并返回FLT_PREOP_PENDING来pend一个I/O操作.返回这个状态值表示minifilter驱动将保留I/O操作的控制直到它调用 FltCompletePendedPreOperation来恢复对I/O操作的处理.

Minifilter驱动的pre-oper callback例程通过执行以下步骤来pend一个I/O操作:

1. 通过调用像FltQueueDeferredIoWorkItem这样的一个例程来发送I/O操作给一个系统工作队列.
2. 返回FLT_PREOP_PENDING.

必须pend所有(或大多数)引入的I/O操作的Minifilter驱动不应使用像 FltQueueDeferredIoWorkItem这样的例程来pend操作,因为调用此例程会引起系统工作队列溢出.换句话说,这样的minifilter驱动应使用一个安全取消队列.更多关于使用安全取消队列的信息参考FltCbdqInitialize.

注意如果以下条件为真则对FltQueueDeferredIoWorkItem的调用将会失败:

操作不是IRP-I/O操作.

操作是一个分页I/O操作.

当前线程的TopLevelIrp域不为NULL. (更多关于如何找出此域的值的信息参考IoGetTopLevelIrp.)

I/O操作的目标实例正被拆卸.

如果minifilter驱动的pre-oper callback 例程返回FLT_PREOP_PENDING, 则它必须在出参CompletionContext 中返回NULL.

Minifilter驱动只可为IRP-I/O操作返回FLT_PREOP_PENDING .要决定一个操作是否为IRP-I/O操作,使用FLT_IS_IRP_OPERATION宏.

出列和处理I/O操作的工作例程必须调用 FltCompletePendedPreOperation来恢复对操作的处理.


5.4 写Post-oper Callback例程


文件系统minifilter驱动用一个或多个post-opera callback例程来过滤I/O操作.

Post-oper callback例程 与legacy FSFD中使用的完成例程类似.

Minifilter驱动为某个特定类型的I/O操作注册一个post-oper callback例程的方法与它为之注册一个pre-oper callback例程的方法相同—就是说,都是通过把callback例程的入口点存储到FLT_REGISTRATION结构(它将作为参数被传给DriverEntry例程中FltRegisterFilter)的OperationRegistration 成员来实现的.

Minifilter仅接收它们已经为之注册了一个pre-oper或post-oper callback例程的I/O操作.minifilter驱动可以为一个给定类型的I/O操作只注册一个pre-oper或只注册一个post-oper callback例程.

Post-oper callback例程的定义如下:

typedef FLT_POSTOP_CALLBACK_STATUS
(*PFLT_POST_OPERATION_CALLBACK) (
    IN OUT PFLT_CALLBACK_DATA Data,
    IN PCFLT_RELATED_OBJECTS FltObjects,
    IN PVOID CompletionContext,
    IN FLT_POST_OPERATION_FLAGS Flags
    );

和完成例程一样,post-oper callback例程在IRQL <= DISPATCH_LEVEL及任意线程的上下文中被调用.

因为它在DISPATCH_LEVEL被调用,所以它不能调用必须在更低 IRQL调用的内核模式例程,如FltLockUserBuffer或RtlCompareUnicodeString.因为同样的原因,post-oper callback例程中使用的一切数据结构都必须从非分页池中分配.

以下情形是上述规则的几个例外:

如果minifilter驱动的pre-oper callback例程为一个IRP-I/O操作返回了FLT_PREOP_SYNCHRONIZE,相应的post-oper callback例程就会在IRQL <= APC_LEVEL及在与pre-oper callback例程相同的线程上下文中被调用.

Fast I/O操作的post-oper callback例程在IRQL = PASSIVE_LEVEL及在与pre-oper callback例程相同的线程上下文中被调用.

Post-create callback例程在IRQL = PASSIVE_LEVEL及发起这个IRP_MJ_CREATE操作的线程上下文中被调用.

当filter管理器为一个给定的I/O操作调用某个minifilter驱动的post-oper callback例程时,该minifilter驱动临时地控制此I/O操作.这个minifilter驱动在它做了以下事情之一时才会返回控制权:

从post-oper callback例程中返回FLT_POSTOP_FINISHED_PROCESSING.

从已经处理了一个在post-oper callback例程中被pend的IRP-I/O操作的工作历程中调用FltCompletePendedPostOperation.

5.4.1 执行一个I/O操作的完成处理


minifilter驱动的post-oper callback例程会在底层文件系统或legacy过滤器或minifilter驱动实例栈中有lower altitude的另外一个minifilter驱动已经完成了一个I/O操作时被调用.

另外,当某个minifilter驱动实例正被拆卸时,filter管理器会"耗尽"该实例已经接收到其pre-oper callback而正等候其post-oper callback的I/O操作.在此情形之下,即使这个I/O操作还没有被完成filter管理器也会调用该minifilter驱动的post-oper callback例程并在入参Flag中设置标记 FLTFL_POST_OPERATION_DRAINING .

当设置了FLTFL_POST_OPERATION_DRAINING标记时,该minifilter驱动一定不可以执行常规的完成处理.它应该只执行必要的cleanup,比如释放它在pre-oper callback例程中的CompletionContext参数分配的内存并返回FLT_POSTOP_FINISHED_PROCESSING.

确保完成处理的执行在安全的IRQL

如写Post-oper Callback例程一文中提到的, IRP-I/O操作的post-oper callback例程可以在DISPATCH_LEVEL调用,除非minifilter驱动的pre-oper callback例程通过返回FLT_PREOP_SYNCHRONIZE同步了这个操作或此操作是一个create操作(create操作本来就是同步的). (更多关于这个返回值的信息,参考 返回FLT_PREOP_SYNCHRONIZE.)

不过,对还没有被同步的IRP-I/O操作来说,minifilter驱动可以两种技术来确保完成处理的执行在IRQL <= APC_LEVEL.

第一种技术是post-oper callback例程pend I/O操作直到完成例程可以在IRQL <= APC_LEVEL被执行.此技术的描述参考文章在post-oper callback例程中pend一个I/O操作.

第二种技术是post-oper callback例程调用FltDoCompletionProcessingWhenSafe. 仅当当前IRQL >= DISPATCH_LEVEL时FltDoCompletionProcessingWhenSafe才会pend I/O操作.否则,这个例程会直接执行minifilter驱动的SafePostCallback 例程. 此技术的描述参考文章FltDoCompletionProcessingWhenSafe.


5.4.2 在post-oper callback例程中pend一个I/O操作


Minifilter驱动的post-oper callback例程 可以通过执行以下步骤来pend一个I/O操作:

1. 调用FltAllocateDeferredIoWorkItem来为此I/O操作分配一个工作项.
2. 调用 FltQueueDeferredIoWorkItem来把此I/O操作发给一个系统工作队列.
3. 返回FLT_POSTOP_MORE_PROCESSING_REQUIRED.

注意如果以下条件为真则对FltQueueDeferredIoWorkItem的调用将会失败:

该操作不是IRP-I/O操作.

该操作是分页I/O操作.

当前线程的TopLevelIrp域不为NULL. (更多如何找出此域的值的信息参考IoGetTopLevelIrp.)

该操作的目标实例正被拆卸. (filter管理器通过在post-oper callback例程的入参Flags 中设置FLTFL_POST_OPERATION_DRAINING标记来指示这个情况.)

Minifilter驱动必须准备好处理失败的情形.如果你的minifilter驱动不能处理这样的失败,你就应该考虑使用返回FLT_PREOP_SYNCHRONIZE 中的技术来取代pend该操作.

在minifilter驱动的post-oper callback例程返回FLT_POSTOP_MORE_PROCESSING_REQUIRED之后,filter管理器将不会对该I/O操作进行任何深入处理直到minifilter驱动的工作例程调用 FltCompletePendedPostOperation来把操作的控制权返回给filter管理器. 在此情形之下filter管理器不会做任何深入处理即使工作线程在操作的callback数据结构的IoStatus.Status域中设置了一个失败的NTSTATUS值.

出列和执行该I/O操作的完成的工作例程必须调用 FltCompletePendedPostOperation来把操作的控制权返回给filter管理器.

 

5.4.3 在post-oper callback例程中令一个I/O失败


Minifilter驱动的post-oper callback例程可以令一个成功的I/O操作失败,但是简单地令一个I/O操作失败不会撤销该操作的影响.minifilter驱动要负责执行撤销该操作的一切所需的处理.

例如,minifilter驱动的post-create callback例程可以通过执行以下步骤来令一个成功的IRP_MJ_CREATE操作失败:

1. 调用FltCancelFileOpen来关闭被create操作创建或打开的文件.注意FltCancelFileOpen不会撤销对文件的一切修改.例如, FltCancelFileOpen不会删除一个最新创建的文件或恢复被删减的文件到以前的大小.
2. 设置callback数据结构的IoStatus.Status域为该操作最终的NTSTATUS值.此值必须是一个有效的错误NTSTATUS值,比如 STATUS_ACCESS_DENIED.
3. 设置callback数据结构的IoStatus.Information域为零.
4. 返回FLT_POSTOP_FINISHED_PROCESSING.

当设置callback数据结构的IoStatus.Status域为该操作最终的NTSTATUS值时,minifilter驱动必须指定一个有效的错误NTSTATUS值.注意这个minifilter驱动不可以指定STATUS_FLT_DISALLOW_FAST_IO;只有filter管理器可以使用这个NTSTATUS值.

FltCancelFileOpen的调用者必须运行在IRQL <= APC_LEVEL.不过,一个minifilter驱动可以从post-create callback例程中安全地调用这个例程,因为, 对IRP_MJ_CREATE操作来说,post-oper callback例程在PASSIVE_LEVEL及发起这个create操作的线程的上下文中被调用.


5.5 修改一个I/O操作的参数


Minifilter驱动可以修改一个I/O操作的参数.例如,一个minifilter驱动的pre-oper callback例程可以通过改变操作的目标实例来把一个I/O操作重定向到另一个卷上. 新的目标实例必须是同一minifilter驱动在另一卷上并处于同一altitude的一个实例.

I/O操作的参数在这个操作的callback数据(FLT_CALLBACK_DATA)结构和I/O参数块(FLT_IO_PARAMETER_BLOCK)结构中.minifilter驱动的pre-oper callback例程和post-oper callback例程会在入参Data中获得此操作的callback数据结构指针.callback数据结构的成员Iopb是一个I/O参数块结构(它包含这个操作的参数)的指针.

如果minifilter驱动的pre-oper callback例程修改了某个I/O操作的参数,则minifilter驱动实例栈中在它之下的所有minifilter驱动都会在它们的pre-oper和post-oper callback例程中收到修改后的参数.

已修改的参数不会被当前minifilter驱动的post-oper callback例程或minifilter驱动栈中一切在它之上的minifilter驱动接收到.在所有情况下,一个minifilter驱动的pre-oper和post-oper callback例程都会接收到一个给定的I/O操作的相同的入参值.

在修改了一个I/O操作的参数之后,pre-oper或post-oper callback例程必须指示通过调用FltSetCallbackDataDirty已经这样做了(即修改了参数),除非它已经改变了callback数据结构的IoStatus域的内容.否则,过滤器管理器会忽视对参数值的一切改变. FltSetCallbackDataDirty会在I/O操作的callback数据结构中设置FLTFL_CALLBACK_DATA_DIRTY标记. Minifilter驱动可以调用FltIsCallbackDataDirty测试此标记或调用FltClearCallbackDataDirty来清除这个标记.

如果某个minifilter驱动的pre-oper callback例程修改了一个I/O操作的参数,所有minifilter驱动实例栈中在其之下的minifilter驱动都会在它们的pre-oper和post-oper callback例程的入参Data和FltObjects中收到已修改的参数. (Minifilter驱动不可以直接修改被参数FltObjects指向的FLT_RELATED_OBJECTS结构的内容.不过,如果某个minifilter驱动修改了一个I/O操作的目标实例或目标文件对象,那么filter管理器就会修改要床给lower minifilter驱动的FLT_RELATED_OBJECTS结构中相应的Instance或FileObject成员的值.)

尽管minifilter驱动的pre-oper callback例程做出的一切参数改变都不会被它自己的post-oper callback例程收到,但它的pre-oper callback例程能够把已改变的的参数信息传给它自己的post-oper callback例程.如果pre-oper callback例程通过返回FLT_PREOP_SUCCESS_WITH_CALLBACK或者FLT_PREOP_SYNCHRONIZE来沿着栈向下传递I/O操作,它可以在被出参CompletionContext指向的minifilter驱动定义的结构中存储已改变参数的信息. Filter管理器会把这个结构的指针传到post-oper callback例程的入参CompletionContext中.

更多关于I/O操作的参数的信息参考FLT_CALLBACK_DATA和 FLT_IO_PARAMETER_BLOCK.


5.6 决定一个I/O操作的Buffering方法


与设备驱动一样,文件系统负责在用户模式应用程序和一个系统的设备之间传输数据.操作系统提供了以下三种方法访问数据buffer:

在buffered I/O方法中,I/O管理器从非分页池中为操作分配一个系统buffer.I/O管理器在发起该I/O操作的上下文中,从这个系统buffer中复制数据到应用程序的user buffer中,反之亦然.

在direct I/O方法中,I/O管理器探测并锁住user buffer.然后它创建一个MDL来映射被锁的buffer.I/O管理器在发起I/O操作的线程上下文中访问该buffer.

在neither I/O中,I/O管理器既不分配一个系统buffer也不锁住或映射 user buffer.而是简单地把buffer的原始用户空间虚拟地址传给文件系统栈.驱动负责确保它们正执行在发起线程的上下文中且buffer地址是有效的.

Minifilter驱动必须在试图使用这些地址之前验证其在用户空间中是否有效. I/O管理器和filter管理器不会验证这样的地址也不会验证内含在要被传给minifilter驱动的buffer中的指针.
所有标准的微软文件系统对大多数I/O处理都使用NEITHER I/O.
更多关于buffering方法的信息,参考Methods for Accessing Data Buffers.

对IRP-I/O操作来说,所使用的buffering方法是操作指定的且由以下要素决定:

正被执行的I/O操作的类型

文件系统卷的DEVICE_OBJECT 结构的成员Flags的值

对IOCTL和FSCTL操作来说,当IOCTL或FSCTL被定义时,被传给CTL_CODE宏的参数TransferType 的值

有buffer的Fast I/O操作总是用NEITHER I/O.

文件系统callback操作没有buffer.


5.6.1 可以是IRP-I/O或Fast I/O的操作


以下类型的操作可以是基于IRP或fast I/O的操作:

IRP_MJ_DEVICE_CONTROL. (注意IRP_MJ_INTERNAL_DEVICE_CONTROL总是基于IRP的.)

IRP_MJ_QUERY_INFORMATION. 如果FileInformationClass参数为FileBasicInformation、FileStandardInformation或FileNetworkOpenInformation则这个操作可以是fast I/O.

IRP_MJ_READ. Minifilter驱动可以在FLT_OPERATION_REGISTRATION结构中设置FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO标记来避免收到fast I/O IRP_MJ_READ操作和cached 基于IRP的读.

IRP_MJ_WRITE. Minifilter驱动可以在FLT_OPERATION_REGISTRATION结构中设置FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO标记来避免收到fast I/O IRP_MJ_WRITE操作和cached基于IRP的写.

当所有这些操作都是fast I/O操作时,它总是使用NEITHER I/O,即使等价的基于IRP的操作使用不同的buffering方法.

当IRP_MJ_DEVICE_CONTROL是一个fast I/O操作时,它总是使用NEITHER I/O而不管IOCTL的传输类型是什么.

尽管IRP_MJ_LOCK_CONTROL可以是一个基于IRP或fast I/O的操作,但它没有buffer.


5.6.2 遵从Device Object Flags的IRP-I/O操作


以下IRP-I/O操作所使用的buffering方法由文件系统卷的DEVICE_OBJECT 结构的成员Flags的值决定:

IRP_MJ_DIRECTORY_CONTROL

IRP_MJ_QUERY_EA

IRP_MJ_QUERY_QUOTA

IRP_MJ_READ

IRP_MJ_SET_EA

IRP_MJ_SET_QUOTA

IRP_MJ_WRITE

在Flags成员中使用的DO_BUFFERED_IO和DO_DIRECT_IO标记的使用如下:

如果设置了DO_BUFFERED_IO,则操作使用buffered I/O.

如果设置了DO_DIRECT_IO且没有设置DO_BUFFERED_IO,则操作使用direct I/O.

如果两个标记都没有设置,则操作使用NEITHER I/O.

更多设备对象标记的信息,参考DEVICE_OBJECT and Initializing a Device Object.

注意IRP_MJ_READ和IRP_MJ_WRITE可以是基于IRP或fast I/O的操作.当它们基于IRP时,buffering方法由以上描述的设备对象标记决定.当它们是fast I/O,它们总是使用neither I/O.更多关于可以是基于IRP或fast I/O的操作的I/O操作的信息,参考可以是IRP-I/O或Fast I/O的操作.


5.6.3 总是使用缓冲 I/O的IRP-I/O操作


以下IRP-I/O操作总是使用buffered I/O,而不管文件系统卷的DEVICE_OBJECT结构的成员Flags的值是什么:

IRP_MJ_CREATE (EaBuffer参数)

IRP_MJ_QUERY_INFORMATION

IRP_MJ_QUERY_VOLUME_INFORMATION

IRP_MJ_SET_INFORMATION

IRP_MJ_SET_VOLUME_INFORMATION

注意IRP_MJ_QUERY_INFORMATION也可以是一个fast I/O操作.当它是一个fast I/O操作时,它用neither I/O.更多关于可以是基于IRP或fast I/O的操作的信息,参考可以是IRP-I/O或Fast I/O的操作.
5.6.4 总是使用既不缓冲也不直接I/O的IRP-I/O操作
以下IRP-I/O操作总是使用既不缓冲也不是直接I/O,而不管文件系统卷的DEVICE_OBJECT结构的成员Flags的值是什么:

IRP_MJ_PNP

IRP_MJ_QUERY_SECURITY

IRP_MJ_SET_SECURITY

IRP_MJ_SYSTEM_CONTROL

5.6.5 基于IRP的IOCTL和FSCTL操作


以下IRP-I/O操作使用相应的缓冲方法,与IOCTL或FSCTL中定义的传输类型相匹配:
* IRP_MJ_DEVICE_CONTROL
* IRP_MJ_FILE_SYSTEM_CONTROL
* IRP_MJ_INTERNAL_DEVICE_CONTROL

传输类型是在CTL_CODE宏的参数TransferType 中指定的. 要获得一个给定的IOCTL或FSCTL的传输类型,使用以下宏:

#define METHOD_FROM_CTL_CODE(ctrlCode)         ((ULONG)(ctrlCode & 3))

这个宏的返回值为以下之一:

#define METHOD_BUFFERED                 0
#define METHOD_IN_DIRECT                1
#define METHOD_OUT_DIRECT               2
#define METHOD_NEITHER                  3

更多关于CTL_CODE宏的信息,参考Defining I/O Control Codes.
注意IRP_MJ_DEVICE_CONTROL也可以是一个fast I/O操作.当它是一个fast I/O操作时,它总是使用neither I/O,而不管IOCTL的传输类型是什么.更多关于IRP_MJ_DEVICE_CONTROL什么时候可以是一个fast I/O操作的信息,参考可以是IRP-I/O或Fast I/O的操作.


5.6.6 没有缓冲的IRP- I/O操作


以下IRP-I/O操作没有buffer因此也没有buffering方法:
* IRP_MJ_CREATE_MAILSLOT
* IRP_MJ_CREATE_NAMED_PIPE
* IRP_MJ_LOCK_CONTROL


5.7 访问一个I/O操作的User Buffers


一个I/O操作的FLT_PARAMETERS结构包含操作的操作指定的参数,包括buffer地址和操作中使用的一切buffer的MDL.

对IRP-I/O操作来说,它的buffer可以通过使用以下来指定:
* 仅MDL (一般为分页I/O)
* 仅Buffer地址
* Buffer地址和MDL

对fast I/O操作来说,只有用户空间buffer地址是指定的. 有buffer的Fast I/O操作总是使用neither I/O因此没有MDL参数.

下文提供了在minifilter驱动的pre-oper和post-oper callback例程中处理基于IRP和fast I/O的操作的buffer地址和MDL的指导方针:


5.7.1 在pre-oper callback例程中访问User Buffers


Minifilter驱动的pre-oper callback例程 应按如下方式来对待IRP-I/O操作中的buffer:
* 检查buffer是否存在一个MDL. MDL的指针可以在操作的参数MdlAddress或OutputMdlAddress中找到. (Minifilter驱动也可以调用FltDecodeParameters来查询MDL指针.)
* 若此buffer存在一个MDL,就调用 MmGetSystemAddressForMdlSafe来获得buffer的系统地址,然后用这个地址来访问buffer.
* 若此buffer没有MDL,就用buffer地址来访问buffer.要确保一个用户空间的buffer地址是有效的,minifilter驱动必须使用像ProbeForRead 或ProbeForWrite这样的例程,在try/except块中封装所有的buffer引用.

Pre-oper callback例程应按如下方式来对待fast I/O操作中的buffer:
* 使用buffer地址访问这个buffer (因为fast I/O操作没有MDL).
* 要确保一个用户空间的buffer地址是有效的, minifilter驱动必须使用像ProbeForRead 或ProbeForWrite这样的例程,在try/except块中封装所有的buffer引用.

对可以是fast I/O或基于IRP的操作来说,所有的buffer引用都应被封装到try/except块中. 尽管你不必在使用buffered I/O的IRP-I/O操作中这样封装这些引用,但try/except块是一中安全预防措施.


5.7.2 在post-oper callback例程中访问User Buffers


Minifilter驱动的post-oper callback例程 应按如下方式来对待IRP-I/O操作中的buffer:
* 检查buffer是否存在一个MDL. MDL的指针可以在操作的参数MdlAddress或OutputMdlAddress中找到. (Minifilter驱动也可以调用FltDecodeParameters来查询MDL指针.)
* 若此buffer存在一个MDL,就调用 MmGetSystemAddressForMdlSafe来获得buffer的系统地址,然后用这个地址来访问buffer. (MmGetSystemAddressForMdlSafe可以在 IRQL <= DISPATCH_LEVEL调用.)
* 若此buffer没有MDL,就用FLT_IS_SYSTEM_BUFFER宏检查此操作有没有设置系统buffer标记.
* 如果FLT_IS_SYSTEM_BUFFER宏返回TRUE,则此操作使用了buffered I/O,且在DISPATCH_LEVEL可安全地访问该buffer.
* 如果FLT_IS_SYSTEM_BUFFER宏返回FALSE,则在IRQL = DISPATCH_LEVEL不可以安全地访问该buffer. 如果post-oper callback 例程可以在DISPATCH_LEVEL调用,它必须调用 FltDoCompletionProcessingWhenSafe来pend这个操作直到它可以在IRQL <= APC_LEVEL被处理.被FltDoCompletionProcessingWhenSafe的SafePostCallback参数指向的callback例程应首先调用FltLockUserBuffer来锁住buffer,然后调用MmGetSystemAddressForMdlSafe获得该buffer的系统地址.

Post-oper callback例程应按如下方式来对待fast I/O操作中的:
* 使用buffer地址访问这个buffer (因为fast I/O操作没有MDL).
* 要确保一个用户空间的buffer地址是有效的, minifilter驱动必须使用像ProbeForRead 或ProbeForWrite这样的例程,在try/except块中封装所有的buffer引用.
* 一个fast I/O操作的post-oper callback例程被担保会在正确的线程上下文中被调用.
* 一个fast I/O操作的post-oper callback例程被担保会在IRQL <= APC_LEVEL被调用,因此它可以安全地调用像FltLockUserBuffer这样的例程.

对可以是fast I/O或基于IRP的操作来说,所有的buffer引用都应被封装到try/except块中. 尽管你不必在使用buffered I/O的IRP-I/O操作中这样封装这些引用,但try/except块是一中安全预防措施.


6. 在Minifilter驱动中管理上下文


上下文是minifilter驱动定义的一个结构,可以与一个filter管理器对象关联起来. Minifilter驱动可以为以下类型的对象创建和设置上下文:
* 文件(仅Vista及以后版本.)
* 实例
* 卷
* 流
* 流句柄(文件对象)
* Transactions (仅Vista及以后版本.)

除了卷上下文必须从非分页池中分配外,上下文可以从分页或非分页池中分配.

当上下文绑定的对象被删除,或minifilter驱动实例从一个卷断开,或minifilter驱动被卸载时,Filter管理器会自动删除上下文.


6.1 注册上下文类型


当minifilter驱动从它的DriverEntry例程中调用FltRegisterFilter时,它必须注册每一种它要使用的上下文的类型.

要注册上下文类型,minifilter驱动得创建一个长度可变的FLT_CONTEXT_REGISTRATION结构组并将其的一个指针存储到FLT_REGISTRATION结构(minifilter驱动将此结构传给FltRegisterFilter的入参Registration)的成员ContextRegistration 中. 此结构组中的元素顺序不分先后.不过,结构组中的最后一个元素必须是{FLT_CONTEXT_END}.

对每一种minifilter驱动使用的上下文类型来说,它必须以FLT_CONTEXT_REGISTRATION结构的形式提供至少一个上下文的定义.每一个FLT_CONTEXT_REGISTRATION结构都定义了上下文的类型、大小和其他信息.

当minifilter驱动调用FltAllocateContext 创建了一个新的上下文时,filter管理器用FltAllocateContext 例程的Size 参数以及FLT_CONTEXT_REGISTRATION 结构的成员Size和Flags 来选择要使用的上下文定义.

对固定尺寸的上下文来说, FLT_CONTEXT_REGISTRATION 结构的成员Size 指定了由minifilter驱动定义的上下文结构的部分的尺寸(以字节为单位).一个上下文的最大尺寸是MAXUSHORT (64 KB). 零是一个有效的尺寸值.filter管理器是用lookaside列表来实现固定大小的上下文的. Filter管理器为每个尺寸值创建两个lookaside列表:一个分页的和一个非分页的.

对尺寸可变的上下文来说,Size成员必须被设置为FLT_VARIABLE_SIZED_CONTEXTS. Filter管理器直接从分页或非分页池汇中分配尺寸可变的上下文.

可以在FLT_CONTEXT_REGISTRATION结构的成员Flags中,指定标记FLTFL_CONTEXT_REGISTRATION_NO_EXACT_SIZE_MATCH. 如果minifilter驱动使用固定尺寸的上下文且指定了此标记,如果上下文的尺寸大于或等于请求的尺寸,filter管理器就会从lookaside列表中分配一个上下文.

对一个给定的上下文类型来说,minifilter驱动可以提供至多三个固定尺寸的上下文定义(其中每一个的尺寸都不同)和一个尺寸可变的定义.更多信息,参考 FLT_CONTEXT_REGISTRATION.

Minifilter驱动可以随意地提供一个上下文cleanup callback例程来在该上下文被free之前被调用.详情参考PFLT_CONTEXT_CLEANUP_CALLBACK.

Minifilter驱动可以随意地定义它自己的分配和free上下文的callback例程,而非依靠filter管理器去执行这些任务.不过, 这是及其必要的. 更多信息,参考 PFLT_CONTEXT_ALLOCATE_CALLBACK和PFLT_CONTEXT_FREE_CALLBACK.

以下从CTX minifilter驱动例子中节选的代码,展示了一个用于注册实例、文件、流和文件对象(流句柄)上下文的FLT_CONTEXT_REGISTRATION结构组.

const FLT_CONTEXT_REGISTRATION contextRegistration[] = {
    { FLT_INSTANCE_CONTEXT,              //ContextType
      0,                                 //Flags
      CtxContextCleanup,                 //ContextCleanupCallback
      CTX_INSTANCE_CONTEXT_SIZE,         //Size
      CTX_INSTANCE_CONTEXT_TAG },        //PoolTag
    { FLT_FILE_CONTEXT,                  //ContextType
      0,                                 //Flags
      CtxContextCleanup,                 //ContextCleanupCallback
      CTX_FILE_CONTEXT_SIZE,             //Size
      CTX_FILE_CONTEXT_TAG },            //PoolTag
    { FLT_STREAM_CONTEXT,                //ContextType
      0,                                 //Flags
      CtxContextCleanup,                 //ContextCleanupCallback
      CTX_STREAM_CONTEXT_SIZE,           //Size
      CTX_STREAM_CONTEXT_TAG },          //PoolTag
    { FLT_STREAMHANDLE_CONTEXT,          //ContextType
      0,                                 //Flags
      CtxContextCleanup,                 //ContextCleanupCallback
      CTX_STREAMHANDLE_CONTEXT_SIZE,     //Size
      CTX_STREAMHANDLE_CONTEXT_TAG },    //PoolTag
    { FLT_CONTEXT_END }
};


6.2 创建上下文


只要某个minifilter驱动已经注册了它要使用的上下文类型,它就可以调用FltAllocateContext 来创建一个上下文.这个例程会依照注册上下文类型一文中描述的标准来选择合适的上下文定义.

以下从CTX minifilter驱动例子中节选的代码中,CtxInstanceSetup例程调用FltAllocateContext 来创建一个实例上下文:

status = FltAllocateContext(
           FltObjects->Filter,           //Filter
           FLT_INSTANCE_CONTEXT,         //ContextType
           CTX_INSTANCE_CONTEXT_SIZE,    //ContextSize
           NonPagedPool,                 //PoolType
           &instanceContext);            //ReturnedContext

在CTX中,以下上下文定义是为实例上下文注册的:

{ FLT_INSTANCE_CONTEXT,              //ContextType
  0,                                 //Flags
  CtxContextCleanup,                 //ContextCleanupCallback
  CTX_INSTANCE_CONTEXT_SIZE,         //Size
  CTX_INSTANCE_CONTEXT_TAG },        //PoolTag

这是一个固定尺寸的上下文定义,因为Size 成员是一个常量. (如果Size 成员是FLT_VARIABLE_SIZED_CONTEXTS则它是尺寸可变的上下文定义.)注意成员Flags中FLTFL_CONTEXT_REGISTRATION_NO_EXACT_SIZE_MATCH标记没有被设置. 在此情形之下, FltAllocateContext 的参数Size值匹配上下文定义的Size成员的值,FltAllocateContext 从相应的非分页lookaside列表中分配实例上下文.如果值不匹配,FltAllocateContext 会失败且返回值为STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND.

FltAllocateContext 初始化新上下文上的引用计数为1.当不再需要此上下文时,minifilter驱动必须release这个引用.因此对FltAllocateContext 的每一次调用都必须匹配一个后来的FltReleaseContext调用.


6.3 设置上下文


在创建了一个新上下文之后,minifilter驱动可以调用FltSetXxxContext (Xxx是上下文的类型)来把它绑定到一个对象上.

如果FltSetXxxContext例程的参数Operation被设置为FLT_SET_CONTEXT_KEEP_IF_EXISTS,那么仅当minifilter驱动还没有为该对象设置一个上下文时FltSetXxxContext 才会绑定最新分配的上下文到该对象上.如果minifilter驱动已经设置了一个上下文, FltSetXxxContext 就会返回STATUS_FLT_CONTEXT_ALREADY_DEFINED(一个NTSTATUS错误代码)且不会取代现有的上下文.如果FltSetXxxContext 例程的OldContext 参数不为NULL,则它会收到现有上下文的指针.当不再需要这个指针时,minifilter驱动必须调用FltReleaseContext来release它.

如果参数Operation 被设置为FLT_SET_CONTEXT_REPLACE_IF_EXISTS,那么FltSetXxxContext 就会绑定新的上下文到对象上.如果minifilter驱动已经设置了一个上下文,FltSetXxxContext 就会删除现有的上下文,设置新的上下文并增加新上下文上的引用计数.如果参数OldContext 不为NULL,它会收到已删除的上下文的指针.当不再需要这个指针时,minifilter驱动必须调用FltReleaseContext来release它.

以下从CTX minifilter驱动例子中节选的代码中,CtxInstanceSetup 例程创建并设置一个实例上下文:

status = FltAllocateContext(
           FltObjects->Filter,           //Filter
           FLT_INSTANCE_CONTEXT,         //ContextType
           CTX_INSTANCE_CONTEXT_SIZE,    //ContextSize
           NonPagedPool,                 //PoolType
           &instanceContext);           //ReturnedContext
...
status = FltSetInstanceContext(
           FltObjects->Instance,              //Instance
           FLT_SET_CONTEXT_KEEP_IF_EXISTS,    //Operation
           instanceContext,                   //NewContext
           NULL);                             //OldContext

if (instanceContext != NULL) {
    FltReleaseContext(instanceContext);
}
return status;

注意在调用FltSetInstanceContext之后,有一个FltReleaseContext 调用来release由FltAllocateContext(不是FltSetInstanceContext)设置的引用计数.其解释在Release上下文一文中.

 


6.4 获得上下文


只要minifilter驱动已经为一个对象设置了一个上下文,它就可以调用FltGetXxxContext 例程来获得该上下文.

以下从SwapBuffers minifilter驱动例子中节选的代码中, minifilter驱动调用FltGetVolumeContext 来获得一个卷上下文:

status = FltGetVolumeContext(
                FltObjects->Filter,    //Filter
                FltObjects->Volume,    //Volume
                &volCtx);              //Context
...
if (volCtx != NULL) {
    FltReleaseContext(volCtx);
}

如果FltGetVolumeContext 的调用成功,参数Context 就会收到调用者的卷上下文的地址. FltGetVolumeContext 会增加Context 指针上的引用计数.因此,当不再需要这个指针时,minifilter驱动必须调用FltReleaseContext 来release它.

6.5 引用上下文


Filter管理器用引用计数来管理一个minifilter驱动上下文的生存期.引用计数是一个指示上下文的状态的数字.只要一个上下文被创建或被一个系统组件引用,它的引用计数就会增加1.当不再需要上下文时,它的引用计数就会被消耗.一个正的引用计数值意味着上下文可用.当引用计数减为零时,该上下文就不可用且filter管理器最终会free它.

Minifilter驱动可以调用FltReferenceContext增加上下文的引用计数来添加它自己对该上下文的引用.这个引用必须最终通过调用FltReleaseContext来移除.


6.6 Releasing上下文


Minifilter驱动调用FltReleaseContext来release一个上下文.对下列例程之一的每一次成功调用都必须最终匹配一个对FltReleaseContext的调用:
FltAllocateContext
FltGetXxxContext
FltReferenceContext

注意FltSetXxxContext返回的OldContext指针和FltDeleteContext返回的Context指针不再需要时也必须被Release.

以下从CTX minifilter驱动例子中节选的代码中, CtxInstanceSetup 例程创建并设置一个实例上下文,然后调用FltReleaseContext:

status = FltAllocateContext(
           FltObjects->Filter,           //Filter
           FLT_INSTANCE_CONTEXT,         //ContextType
           CTX_INSTANCE_CONTEXT_SIZE,    //ContextSize
           NonPagedPool,                 //PoolType
           &instanceContext);           //ReturnedContext
...
status = FltSetInstanceContext(
           FltObjects->Instance,              //Instance
           FLT_SET_CONTEXT_KEEP_IF_EXISTS,    //Operation
           instanceContext,                   //NewContext
           NULL);                             //OldContext

if (instanceContext != NULL) {
    FltReleaseContext(instanceContext);
}
return status;

注意无论FltSetInstanceContext 是否成功都调用了FltReleaseContext.即无论成功与否调用者必须调用FltReleaseContext 来release由FltAllocateContext (不是FltSetInstanceContext)设置的引用.

如果实例的上下文被成功地设置,FltSetInstanceContext就会添加它自己对该实例上下文的引用.因此,不再需要被FltAllocateContext 设置的引用,然后对FltReleaseContext的调用会移除它.

如果对FltSetInstanceContext的调用失败,实例上下文仅有一个引用,也就是由FltAllocateContext设置的那个.当FltReleaseContext 返回时,实例上下文的引用计数会变为零,然后它会被filter管理器free.


6.7 Deleting上下文


每一个FltSetXxxContext 成功设置的上下文都必须最终被删除.不过,当上下文绑定的对象被删除、一个minifilter驱动实例从一个卷上断开或minifilter驱动被卸载时filter管理器会自动删除它们.因此,minifilter驱动极其有必要明确地删除一个上下文.

Minifilter驱动可以调用FltDeleteXxxContext或调用FltDeleteContext来删除一个上下文.

仅当一个上下文当前因为某个对象而被设置时,它才可以被删除.如果它还没有被设置或它已经被FltSetXxxContext调用成功地取代了那它就不能被删除.

在对FltDeleteXxxContext的调用中,如果OldContext 参数不为NULL则它会返回旧的上下文.如果它为NULL,则filter管理器就会消耗上下文上的引用计数,除非minifilter驱动在其上还有未决的引用否则它就会被free.

以下代码说明如何删除一个流上下文:

status = FltDeleteStreamContext(
           FltObjects->Instance,      //Instance
           FltObjects->FileObject,    //FileObject
           &oldContext);              //OldContext
...
if (oldContext != NULL) {
    FltReleaseContext(oldContext);
}

上例中,FltDeleteStreamContext 从流中移除流上下文,但它没有消耗上下文的引用计数,因为OldContext 参数不为NULL. FltDeleteStreamContext 会在参数OldContext中返回已删除的上下文的地址. 在执行完一切所需的处理之后,调用者必须调用FltReleaseContext来release这个上下文.


6.8 Freeing上下文


一个上下文会在它被删除且所有对它的未决引用都已经被release之后被free掉.

这里有此规则的一个例外:如果某个上下文已经被创建但还没有调用FltSetXxxContext,它就不需要被删除.当它的引用计数变为零时它会被free. (参考Release上下文一文中的例子代码.)

当某个minifilter驱动注册了它的上下文类型,每一个上下文定义都可以随意地包括一个在该上下文被free之前会被调用的上下文cleanup callback例程. 更多信息参考PFLT_CONTEXT_CLEANUP_CALLBACK.


6.9 文件系统对上下文的支持


要支持文件上下文(如果适用的话)、流上下文和文件对象(流句柄)上下文,一个文件系统必须使用FSRTL_ADVANCED_FCB_HEADER结构.微软的Windows文件系统都适用了这个结构,所有第三方的文件系统开发者也都被鼓励这么做.详情看FsRtlSetupAdvancedHeader和FSRTL_ADVANCED_FCB_HEADER.

NTFS和FAT文件系统不支持在pre-create中或post-close中或IRP_MJ_NETWORK_QUERY_OPEN操作的分页文件上的文件、流或文件对象上下文.

minifilter驱动可以分别调用FltSupportsStreamContexts和FltSupportsStreamHandleContexts来决定一个文件系统是否支持流上下文和一个给定文件对象的文件对象上下文.
Vista及以后版本上文件上下文可用.

对支持per文件仅一个单一数据流的文件系统(比如FAT)来说,文件上下文等价于流上下文.这样的文件系统通常都支持流上下文但不支持文件上下文.取而代之的是,filter管理器利用文件系统对流上下文的现有的支持提供了这个支持.对绑定到这些文件系统上的minifilter驱动实例来说,FltSupportsFileContexts 返回FALSE, 同时FltSupportsFileContextsEx 返回TRUE (当Instance 参数中是一个非NULL的有效值时).

要支持文件上下文,一个文件系统必须:
* 嵌入一个PVOID类型的FileContextSupportPointer 成员到它的文件上下文结构中,通常是FCB.文件系统必须初始化此成员为NULL.
* 用FsRtlSetupAdvancedHeaderEx(而非 FsRtlSetupAdvancedHeader)来初始化它的流上下文结构,为了参数FileContextSupportPointer传一个有效指针给FileContextSupportPointer成员(被嵌入到相应的文件上下文结构中). 详看FsRtlSetupAdvancedHeaderEx和FSRTL_ADVANCED_FCB_HEADER.
* 当它删除对一个文件的文件上下文结构时,调用FsRtlTeardownPerFileContexts来free已被过滤器和minifilter驱动关联到该文件上的所有的文件上下文结构.


7.9 最优方法


如果minifilter驱动仅为per卷创建一个minifilter驱动实例的话,它应该使用实例上下文而非卷上下文来谋得更高性能.

Minifilter驱动也可以通过存储一个minifilter驱动实例上下文的指针到它的流或流句柄上下文中来提高性能.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值