windows驱动文件流上下文操作一
概述
文件流上下文在微过滤器(Minifilter)程序中能够为文件相关的操作存储有用的信息,在微过滤器的每个文件处理阶段从指向驱动实例的之阵中获取对应文件的文件上下文信息,为驱动中处理文件提供了巨大的便利也提升了驱动处理文件的效率,试想没有文件流上下文,对同一个文件的属性判断要在每个IRP操作中重复判断,那是多么浪费性能。
上下文是系统为驱动实例和处理对象(文件句柄,文件流,卷设备)存储其关联信息而动态开辟的一块内存
本文主要在基于Minifilter基础上,介绍一下文件流上下文如何创建,储存信息,获取上下文,释放上下文。主要操作参考自微软官方例程。
文件流上下文和流句柄上下文区别
学会了如何创建上下文之后,将通过一组实验来说明流上下文和句柄上下文之间的区别,这里先列出结论如下:对于一个上下文的流,可以存在许多流句柄上下文。
注册流上下文
首先要在注册过滤器结构体中定义上下文对象,关注下面的Contexts数组,示例如下:
CONST FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
Contexts, // Context
Callbacks, // Operation callbacks
Unload, // MiniFilterUnload
InstanceSetup, // InstanceSetup
NULL, // InstanceQueryTeardown
NULL, // InstanceTeardownStart
NULL, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL, // NormalizeNameComponent
NULL
};
具体Contexts数组根据需要进行声明,示例定义如下(该示例中定义了文件流上下文和卷上下文):
CONST FLT_CONTEXT_REGISTRATION Contexts[] = {
{
FLT_VOLUME_CONTEXT, // 定义上下文类型,这里为卷上下文
0, // 定义新的上下文如何从旁视链表中分配内存的方式,定义为0,表明仅当请求申请的大小等于定义的结构长度时才分配内存,一般这里选择0就可以了
CleanupContext, // 上下文清理回调函数,这里需要根据自己操作上下文的情况在这个回调函数中释放相应的内存
sizeof(VOLUME_CONTEXT), // 卷上下文结构体大小
MEM_CALLBACK_TAG // 定义申请的上下文内存池标记,一般为1-4个ascii字符
},
{
FLT_STREAM_CONTEXT, // 定义上下文类型,这里为流上下文
0, // 同上
CleanupContext, // 同上
sizeof(STREAM_CONTEXT), // 流上下文结构大小
MEM_FILE_TAG // 同上
},
{ FLT_CONTEXT_END } // 必须这样结尾
};
可以参考上面的注释理解这个上下文注册数组的定义
其中CleanupContext也给出如下示例代码,该回调函数可以拿到上下文的类型,可以根据类型来对不同的上下文进行内存释放
VOID
TransEncCleanupContext(
__in PFLT_CONTEXT pcContext,
__in FLT_CONTEXT_TYPE pctContextType
)
{
PVOLUME_CONTEXT pVolumeContext = NULL;
PSTREAM_CONTEXT pStreamContext = NULL;
PAGED_CODE();
switch (pctContextType) {
case FLT_VOLUME_CONTEXT:
{
pVolumeContext = (PVOLUME_CONTEXT)pcContext;
// 释放上下文,定义你自己的释放函数
MyFctFreeVolumeContext(pVolumeContext );
}
break;
case FLT_STREAM_CONTEXT:
{
pStreamContext = (PSTREAM_CONTEXT )pcContext;
// 释放上下文,定义你自己的释放函数
MyFctFreeStreamContext(pStreamContext );
}
break;
}
}
创建流上下文
status = FltAllocateContext(
FltObjects->Filter, // 过滤器驱动实例
FLT_STREAM_CONTEXT, // 类型
sizeof(STREAM_CONTEXT), // 上下文结构体的大小,根据自己的定义
NonPagedPool, // 非分页内存
pStreamContext); // 返回值,返回的上下文对象指针
创建上下文一定要关联一个释放上下文的函数,释放在下面示例中;
在返回的上下文指针中,就可以进行自己上下文内容相关的操作了,比如记住文件的相关属性信息,或者自己有用的标识
特别注意
在NTFS和FAT文件系统中,不支持预创建(IRP_MJ_CREATE-PRE)、关闭后(IRP_MJ_CLOSE-POST)操作中的文件流上下文操作,文件句柄上下文也是不支持的,所以在实际使用上下文的过程中,建议在创建后(IRP_MJ_CREATE-POST)中获取或者创建文件流上下文
设置流上下文
status = FltSetStreamContext(
pFltObjects->Instance,
pFltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
pStreamContext,
(PFLT_CONTEXT *)&pOldStreamContext);
为流上下文申请空间后,便可以将上下文设置进去,和驱动实例和文件对象进行关联,和文件关联的流上下文自此就有了,可以在后续步骤中获取到流上下文了。
获取流上下文
status = FltGetStreamContext(
FltObjects->Filter, // 过滤器驱动实例
FltObjects->FileObject, // 文件对象
pStreamContext); //Context
...
if (pStreamContext!= NULL) {
FltReleaseContext(pStreamContext);
}
释放(Release)文件流上下文
if (pStreamContext!= NULL) { // pStreamContext为指向流上下文的指针
FltReleaseContext(pStreamContext);
}
这里的释放上下文仅仅是对上下文的引用计数减1,并没有实际释放上下文所占用的内存,实际释放内存是在注册时的回调函数cleanup函数中进行。在内核中,很多时候用Release标识应用计数减1,带有Free的API是真正将内存清空的操作。
如有谬误,烦请指正,谢谢!