文件系统Minifilter驱动(十)

来源:互联网 发布:手机壳定制软件 编辑:程序博客网 时间:2024/04/30 05:58

在Minifilter驱动中管理上下文

上下文是minifilter驱动定义的一个结构,可以与一个filter管理器对象关联起来. Minifilter驱动可以为以下类型的对象创建和设置上下文: 

· 文件(仅Vista及以后版本.) 

· 实例

· 

· 

· 流句柄(文件对象) 

· Transactions (仅Vista及以后版本.) 

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

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

本节包括: 

一、注册上下文类型 

当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 结构的成员SizeFlags 来选择要使用的上下文定义. 

对固定尺寸的上下文来说, 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_CALLBACKPFLT_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 }
};

二、创建上下文 

只要某个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调用. 

三、设置上下文

在创建了一个新上下文之后,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上下文一文中. 

四、获得上下文 

只要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它. 

五、引用上下文 

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

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

六、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. 

七、删除上下文 

每一个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这个上下文. 

八、Freeing上下文 

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

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

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

九、文件系统对上下文的支持 

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

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

minifilter驱动可以分别调用FltSupportsStreamContextsFltSupportsStreamHandleContexts来决定一个文件系统是否支持流上下文和一个给定文件对象的文件对象上下文. 

Vista及以后版本上文件上下文可用. 

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

要支持文件上下文,一个文件系统必须: 

· 嵌入一个PVOID类型的FileContextSupportPointer 成员到它的文件上下文结构中,通常是FCB.文件系统必须初始化此成员为NULL. 

· FsRtlSetupAdvancedHeaderEx(而非 FsRtlSetupAdvancedHeader)来初始化它的流上下文结构,为了参数FileContextSupportPointer传一个有效指针给FileContextSupportPointer成员(被嵌入到相应的文件上下文结构中). 详看FsRtlSetupAdvancedHeaderExFSRTL_ADVANCED_FCB_HEADER

· 当它删除对一个文件的文件上下文结构时,调用FsRtlTeardownPerFileContexts来free已被过滤器和minifilter驱动关联到该文件上的所有的文件上下文结构.

十、最优方法 

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

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

原创粉丝点击