微过滤器驱动开发指南.docx
- 文档编号:4651595
- 上传时间:2022-12-07
- 格式:DOCX
- 页数:38
- 大小:42.19KB
微过滤器驱动开发指南.docx
《微过滤器驱动开发指南.docx》由会员分享,可在线阅读,更多相关《微过滤器驱动开发指南.docx(38页珍藏版)》请在冰豆网上搜索。
微过滤器驱动开发指南
Windows文件系统过滤管理器之
微过滤器驱动开发指南
0.译者序
对我来说,中文永远是最美,最简洁,最精确和最高雅的文字。
本文翻译仅仅用做交流学习。
我不打算保留任何版权或者承担任何责任。
不要引用到赢利出版物中给您带来版权官司。
本文的翻译者是楚狂人,如果有任何问题,你可以通过邮箱MFC_Tan_Wen@,或者是QQ16191935,或者是MSNwalled_river@与我交流。
我翻译此文出于对文件系统技术的兴趣。
这就是新的文件系统过滤接口。
其实也不算什么新的东西,微软开发了另一个“旧模型的”过滤驱动,称之为过滤管理器(FilterManager)。
从而提供了一系列新的接口来让你开发新的过滤器。
确实这套接口变简单清晰了。
你至少避免了包含无数个信息的IRP,避免了请求在各个部件中循环的发来发去,一个分发例程中处理无数中情况,一不小心系统崩溃。
我不知道花了多少时间才弄明白一个简单的缓冲读请求从用户到过滤到文件系统和缓冲管理器,虚拟内存管理器之间的关系!
现在你也许不需要再管他们了,仅仅做好自己的过滤工作就可以。
这套接口强大吗?
能实现你想要的功能吗?
你很快就发现你没有研究过sfilter就看不懂Minifilter,或者是还得从sfilter开始做起更灵活一点。
微软就是这样,拿僵硬而且也不简单的东西来“简化”强大灵活但是设计上一团糟的东西,对于你来说是两者都必须学习,最后你的脑子被微软塞得满满的,不过没关系,我们已经习惯了.
此文的原文是《FilterDriverDevelopmentGuide》,出自微软的网站。
我在以下这个地址下载得到此文:
我尽量在翻译中使文章保持原貌。
如果您认为此文无法理解,建议您首先阅读旧的文件过滤驱动的相关资料。
我认为必须有文件系统和windows驱动的相关知识,才能阅读此文。
我未必总是使用规范的名词,但我总是使用最容易理解的名词。
一些常用的可能不翻译,比如IRP,MDL,有驱动开发经验的人应该可以理解。
另一些可能采用中文(英文)的方式。
一些解释如下:
例程(Routine):
我不懂得例程和函数有什么不同。
我认为例程就是函数。
称为Routine而不是Function可能是为了避免其他c程序员理解得太容易。
接口(Api):
编程开发接口,一个提供给你调用的函数。
流(Stream):
如果你写过文件系统驱动,那么你一定知道FileObject,对你来说一个流就是一个FileObject。
文件(File):
一个文件可能有多个流,因为可能多次打开,多个FileObject可能对应一个文件。
域(Field):
一个数据结构中的一个数据成员。
喜欢数据库的人可能称为字段。
喜欢面向对象的称为数据成员。
透明(transparence):
看不见,意味着也不需要管。
不过请注意透明的反意词绝对不是不透明(opacity)。
不透明(opacity):
不知道的。
比如空指针。
因为空指针指向的是什么,从空指针本身是了解不到的。
所以称为不透明的指针。
回调(Callback)函数:
一个由系统调用而且原则上你不能自己调的函数。
预操作(pre-operation)回调:
如果打算过滤一个操作,那么这个回调出现在操作完成之前。
后操作(post-operation)回调:
如果打算过滤一个操作,那么这个回调出现在操作完成之后。
1.概述
这个文档用于I/O管理器和基本文件系统之间的过滤驱动。
文件系统可能是本地或者网络的。
这个文档不涉及文件系统和存储设备之间的过滤驱动,比如FtDisk和DMIO.
我们将主要讨论一种新的文件系统过滤驱动模型,所谓的微过滤器(minfiter).
以前的文件系统过滤基于一个例子sfilter.使用IRP和设备对象进行过滤。
我们现在称之为“旧过滤模型”
新的架构中一个关键的组件其实是一个旧过滤模型的文件系统过滤驱动,被称为“过滤管理器(FilterManger)”.在未来,微软发行的操作系统将默认安装这个驱动。
(译者注:
现在,你得手工安装。
)这个驱动通过提供一些库供微过滤器调用来管理所有的微过滤器。
必要的头文件,库和二进制代码都在微过滤器IFSKit中。
为何要开发一个微文件系统过滤驱动?
.通过更少的工作量,得到更简单的,更可靠的过滤驱动.
.动态加载和卸载,绑定和解除绑定.
.在过滤栈中,绑定到一个合理确定的位置。
.上下文管理。
快捷,干净,可靠的上下文管理,用于文件对象,流,文件,实例和卷.
.一组有用的调用.包括根据文件名寻找,高效存取,和用户态程序之间的通信,以及io排队.
.支持非回环I/O.这样,一个微过滤器发起的I/O请求可以轻松的只让栈中更下面的微过滤器以及文件系统看到了。
.仅仅过滤感兴趣的操作。
不象旧过滤模型那样必须挂接每个操作入口以便把操作传递到下层.
2.术语
在过滤管理器架构中,定义了一些新的对象。
为了搞清这些,这里将列出一些定义:
过滤器:
在文件系统上执行一些过滤操作的一种驱动.
卷:
在本地文件系统,这个对象指文件系统所管理的逻辑卷.对于网络重定向文件系统,指所有网络请求被重定向的目的。
卷直接对应文件系统(无论本地或者网络)旧过滤模型中的设备对象(DEVICE_OBJECT).
实例:
一个过滤器在一个卷上唯一的某层上生成的一个实例化对象。
过滤管理器把所有的IO请求发到卷上的实例栈上。
一个微过滤器在一个卷上可能不止一个实例.规范的例子是FileSpy.有时候把FileSpy的两个实例分别绑定在另一个过滤器的上边和下边。
此时每个实例有一个私有的上下文.这个上下文包含IO操作的日志.可以用来比较一个过滤器上下的IO操作有什么不同。
文件:
文件系统保存在一个磁盘上的可能包含若干个流的有名字的数据对象.
流:
指一个文件中的物理数据流.
文件对象(FileObject):
用来描述一个用户对一个文件中的一个物理数据流的一次打开。
回调数据(CallbackData):
过滤管理器中的一种数据结构,包含了一个操作中的所有信息。
对应于旧过滤模型中的IRP.
3.微过滤器安装
微过滤器可以通过一个INF文件安装。
INF文件指出了这个微过滤器所支持的实例.实例的具体说明在第5节.每个实例有一组标志,还有一个唯一的数值固定了它在过滤栈中的位置。
INF文件中有一个表标明了每个实例的层级.这用来给文件系统过滤的开发商装载他们的微过滤器.有标记标明了这个微过滤器是否需要"自动的绑定".如果是,那么每个新的卷出现的时候,微过滤器都回收到一个通知.它可以在此绑定他们.绑定的时候,inf文件中的层级决定了绑定到什么层次上。
在微过滤器运行时,文件系统过滤开发商也可以在某个指定的层级上动态的生成一个实例,这可以使用FilterAttachAtAltitude()调用.这对于开发者来说可以用来进行测试和排除bug.
4.微过滤器注册
微过滤器是内核驱动。
因此它必须导出一个名为DriverEntry的函数。
在驱动加载的时候这个函数第一个被调用.很多微过滤器在DriverEntry()中调用FltRegisterFilter().
FilterRegisterFilter()需要传入一个参数。
是一个FLT_REGISTRATION结构.包含了:
一个卸载例程.实例通知回调,一组上下文回调指针,一组文件系统操作回调指针.一般情况下,微过滤过器只捕获一部分操作,因此文件系统操作回调指针可能并不多。
对于某一种操作,微过滤器可以指定一些附加的标记来指明它是否在所有的情况下都收到它们.比如,如果FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO被指定了,微过滤器就不会收到任何此类IRP的pagingI/O操作。
同样的,如果FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO被指定了,那么只有这类操作的非缓冲请求能看见.(比如说,指定了IRP_MJ_READ类操作,那么所有的缓冲读就都不会被微过滤器捕获了。
5.开始过滤
当一个微过滤器注册自己,它就应该在某个时间调用函数FltStartFiltering()来开始过滤。
并不一定要在DriverEntry中调用。
不过大多数微过滤器可能是这样做的。
这个函数将激发必要的通知,导致微过滤器绑定到卷上然后开始过滤I/O操作。
为此,过滤管理器会通过微过滤器的inf文件遍历它注册过的所有的实例。
每个示例都有一个层级。
一个层级是一个唯一的字符串,(如"100.123456"),这个定义了微过滤器的这个实例在栈上的位置。
商业版本的微过滤器层级将由微软公司来分配。
层级的数字越高,这个微过滤器绑定在栈上的位置就越高。
一些示例层级提供给开发者用来实现微过滤器。
这些是仅有的不会被分配的层级。
层级有两个作用:
一是确定两个微过滤器之间的顺序关系,尤其是有时得实现一些不用去考虑个别微过滤器什么时候加载的功能。
比如说,一个加密解密过滤器必须安装在一个防病毒的过滤的下边。
否则,防病毒过滤器无法从已经加密的内容中发现病毒。
另外就是提供了一个最小的测试矩阵,用来测试这些过滤驱动的互容性。
如果这些驱动实例都是按一个指定的顺序在栈中的,那么测试的时候就不用再考虑排列各种不同的顺序了。
在inf文件中,一个实例和一个标记联系在一起。
如果第1个位标记了,那么微过滤器不会在卷出现在系统中的时候得到通知。
这样的实例应该通过过滤管理器编程接口来手工的绑定。
如果第2位被设置了,即使手工的发送了一个绑定请求,微过滤器也不会收到通知来要求绑定一个实例到一个卷。
6.实例的通知
当一个实例生成的时候,一组回调函数提供来通知微过滤器。
通过这些回调,微过滤器可以决定它的实例在什么时候绑定到卷上和从卷上解除了绑定。
6.1.安装一个实例
回调例程InstatanceSetupCallback()在下列情况下被调用:
.当一个微过滤器加载的时候,每个存在的卷都会导致这个调用。
.当一个新的卷被mount.
.当FltAttachVolume被调用(内核模式)
.当FltAttachVolumeAtAltitude()被调用(内核模式)
.当FilterAttach()被调用(用户模式)
.当FilterAttachAtAltitude()被调用(用户模式)
在这个过程中,微过滤器决定是否在这个卷上生成实例。
这个回调的原型如下:
typedefNTSTATUS
(*PFLT_INSTANCE_SETUP_CALLBACK)(
INPCFLT_RELATED_OBJECTSFltObjects,
INFLT_INSTANCE_SETUP_FLAGSFlags,
INDEVICE_TYPEVolumeDeviceType,
INFLT_FILESYSTEM_TYPEVolumeFilesystemType
);
FltObjects结构喊有指向微过滤器,卷,和实例的指针。
这个实例指将要在InstanceSetupCallback()函数中生成的实例。
Flags标记是什么操作导致激发了InstanceSetupCallback():
FLTFL_INSTANCE_SETUP_AUTOMATIC_ATTACHMENT:
这是一个微过滤器注册的时候,一个自动的绑定通知。
过滤管理器为每个刚加载的微过滤器枚举所有的卷。
如果是一个使用者明确的指定一个实例绑定到某一个卷,不会设置有这个标记。
FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT:
通过调用FilterAttach()(用户态),或者是FilterAttachVolumeAtAltitude()(用户态),或者是FltAttachVolume()(内核态)所发起的一个手工的请求。
FLTFL_INSTANCE_SETUP_NEWLY_MOUNTED_VOLUME:
文件系统刚刚挂载(mount)了一个卷,所以呼叫InstanceSetupCallback()来通知微过滤器,如果它愿意可以生成实例来绑定这个卷。
在InstanceSetupCallback()中,微过滤器同时得到了卷设备类型(VolumeDeviceType)和卷文件系统类型(VolumeFilesytemType),用以判断这个卷是否过滤器所感兴趣的。
同时,微过滤器可以调用FltGetVolumeProperties()来获取卷属性。
通过FltSetInstanceContext()在实例上设置上下文。
当然这是需要绑定的时候。
它甚至可以在卷上打开或者关闭文件。
如果这个回调返回了成功,那么这个实例将绑定到卷上。
如果返回了一个警告或者错误,那么不会绑定。
如果微过滤器没有指定InstanceSetup回调,那么,系统将认为用户总是返回了STATUS_SUCCESS,实例总是会生成并绑定。
6.2.控制实例的销毁
InstanceQueryTeardown()回调仅仅在一个手工解除绑定的请求下被调用。
以下操作可能导致:
FltDetachVolume()(内核模式)
FilterDetach()
(用户模式)
如果一个微过滤器没有提供这个回调,那么手工解除绑定是不被支持的。
但是,卷的解挂载(dismount)和微过滤器的卸载还是允许的。
如果这个回调返回成功,那么过滤管理器开始销毁给出的实例。
最后实例的InstanceTeardownStart()和InstanceTeardownComplete()会被调用。
如果返回了错误或者警告,手工解除绑定会失败。
推荐的错误代码有:
STATUS_FLT_DO_NOT_DETACH,不过实际上你可以返回任何错误代码。
InstanceQueryTeardown()回调的原型是:
typedefNTSTATUS
(*PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK)(
INPCFLT_RELATED_OBJECTSFltObjects,
INFLT_INSTANCE_QUERY_TEARDOWN_FLAGSFlags
);
和InstanceSetupCallback()类似,FltObject指出了与这个销毁操作有关的微过滤器,卷和实例。
6.3.实例解绑定的同步
如果InstanceTeardownStart()的时候已经决定要解除绑定,那么这个例程中必须做以下的事情:
(1)重设所有的未决的I/O操作(包括预操作和后操作)
(2)保证不会有新的I/O操作进入未决。
(3)对刚刚到达的操作开始最少的工作。
同时,应该做以下操作:
(1)关闭所有打开的文件。
(2)取消所有本过滤器发起的I/O请求。
(3)停止将新的工作任务排队。
然后微过滤器把控制权交还过滤管理器来继续它的销毁过程。
当所有与这个实例相关的操作都排除干净或者完成了,InstanceTeardownComplete()会被调用。
管理器保证此时所有此实例的存在的操作回调都完成了。
这时微过滤器必须关闭所有这个实例打开的文件。
这两个回调的原型如下:
typedefVOID
(*PFLT_INSTANCE_TEARDOWN_CALLBACK)(
INPCFLT_RELATED_OBJECTSFltObjects,
INFLT_INSTANCE_TEARDOWN_FLAGSReason
);
FltObjets中有微过滤器,卷和实例。
Resson参数指明这次销毁的原因,可能是以下一些标记的组合:
FLTFL_INSTANCE_TEARDOWN_MANUAL:
这次销毁操作是一个手工的请求。
(FilterDetach()或者FltDetachVolume()).
FLTFL_INSTANCE_TEARDOWN_FILTER_UNLOAD:
这次销毁操作是因为微过滤器执行卸载或者是选择了把卸载请求失败掉导致的。
FLTFL_INSTANCE_TEARDOWN_MANDATORY_FILTER_UNLOAD:
这次销毁操作是一次强制卸载导致的。
这种情况下不能把卸载请求失败掉。
FLTFL_INSTANCE_TEARDOWN_VOLUME_DISMOUNT:
这次销毁是一个卷被解挂载的结果。
FLTFL_INSTANCE_TEARDOWN_INTERNAL_ERROR:
这次销毁是因为安装实例的时候的一个内部错误导致的,比如内存不足。
请注意没有返回值。
InstanceTeardownStart()和InstanceTeardownComplete()都不能失败。
过滤管理器保证这些例程都运行在PassiveIRQL.
7.回调支持
7.1回调数据(Callbackdata)
回调数据(Callbackdata)是过滤管理器用来描述I/O操作的新结构。
类似旧过滤模型下的IRP.微过滤器通过这个结构和过滤管理器交互。
不同的是,回调数据不像IRP那样管理一个栈结构。
回调数据的管理都通过已经明确定义的过滤管理器接口。
并且返回状态值给过滤管理器即可。
FLT_CALLBACK_DATA类型包含了微过滤器描述一个I/O操作所需要的所有的信息。
下面继续详细讲解这个结构中的各个域来说明其中包含的信息:
Flags:
提供这个操作的一些信息。
一个或多个下面的标记可能被设置在Flags中:
FLTFL_CALLBACK_DATA_IRP_OPERATION:
这个回调数据描述一个IRP操作。
FLTFL_CALLBACK_DATA_FAST_IO_OPERATION:
这个回调数据描述一个FastIO操作。
FLTFL_CALLBACK_DATA_FS_FILTER_OPERATION:
这个回调描述一个文件系统过滤器操作。
FLTFL_CALLBACK_DATA_SYSTEM_BUFFER:
这个操作所用的缓冲是一个系统分配的缓冲。
FLTFL_CALLBACK_DATA_GENERATED_IO:
这个操作是由一个微过滤器发起的。
FLTFL_CALLBACK_DATA_REISSUED_IO:
这个操作被一个当前实例之上的过滤器所重新发回给文件系统。
FLTFL_CALLBACK_DATA_DRAINING_IO:
只有设置了后操作(Post-operation)回调的情况下,表明这是一个快速“排出”的I/O操作以便微过滤器的卸载。
FLTFL_CALLBACK_DATA_POST_OPERATION:
只有设置了后操作(Post-operation)回调的情况下,表明着个I/O正在后操作中。
FLTFL_CALLBACK_DATA_DIRTY:
当一个微过滤器已经改变了这个操作的一个或者多个可变参数的时候,设置这个参数。
这个标记仅仅在Pre-operation过程中设置。
微过滤器必须用FLT_SET_CALLBACK_DATA_DIRTY()和FLT_CLEAR_CALLBACK_DATA_DIRTY()来操作这个标记。
Thread:
发出这个操作的线程的地址。
Iopb:
指向这个操作的可变参数的指针。
这个结构在后边详叙。
IoStatus:
IO_STATUS_BLOCK结构返回操作最后的状态。
如果一个微过滤器打算结束这个操作,那么必须先设置这个域,然后才能结束这个请求。
对于传递给文件系统去的请求,在后操作过程(Post-operation)中有操作最终的状态。
TagData:
仅仅在Create操作的后操作回调中有效。
当一个操作的目标文件有一个重解析点(Reparsepoint)的时候设置这个位。
QueueLinks:
一个链表入口结构。
有时要把回调数据(CallbackData)放入工作队列中使用这个。
QueueContext[2]:
一组空指针结构,用来传入附加的上下文到工作队列处理过程中。
FilterContext[4]:
一组空指针结构,当回调数据进入了队列,微过滤器可以做任意使用。
不依赖于过滤管理器的内部结构。
RequestorMode:
这个操作的者的请求模式。
Iopb域所指的是一个FLT_IO_PARAMETER_BLOCK结构。
包含了回调数据中可以修改的部分。
对比IRP来说,这里相当于IRP的当前栈空间(currentstacklocation)。
微过滤器必须访问这个结构来得到每次预操作(pre-operation)和后操作(post-operation)回调的I/O参数。
下面是一些更详细的细节:
IrpFlags:
IRP中描述这个操作的一些标记。
MajorFunction:
IRP主功能号。
MinorFunction:
IRP辅功能号。
OperationFlags:
即IO_STACK_LOCATION.Flags.
TargetFileObject:
这个操作所影响到的目标文件。
TargetInstance:
管理这个操作的实例。
Parameters:
FLT_PARAMETERS是一个共用体。
描述主功能号和辅功能号所指定的操作的具体参数。
除了在预操作回调中不能修改主功能号之外,微过滤器可以修改这个结构中其他的任何参数。
如果参数改变,微过滤器应该调用FLT_SET_CALLBACK_DIRTY()来注明这个改变。
更多详细的信息将在第8节中讲述。
微过滤器在同一个I/O操作的预回调和后回调中,总是会看到参数是一样的。
即使下面的过滤器可能已经修改了这些参数。
这是由过滤管理器保证的。
但是虽然FLT_IO_PARAMTER_BLOCK的内容是一样的,在预操作和后操作中,这个结构的地址可能不一样。
因此微过滤器不应该依赖这个地址。
回调数据结构包含IO_STATUS_BLOCK来记录这个操作的状态。
过滤管理器会“尊重”这些改变而不会标记这些数据为脏(Dirty)。
微过滤器如果打算在预操作回调中结束这个操作或者是后操作回调中撤消这个操作,都必须先设置这个IO_STATUS_BLOCK。
7.2预操作回调(Pre-OperationCallbacks)
所有的预操作回调原型都是这样:
typedefFLT_PREOP_CALLBACK_STATUS
(*PFLT_PRE_OPERATION_CALLBACK)(
INOUTPFLT_CALLBACK_DATAData,
INPCFLT_RELATED_OBJECTSFltObjects,
OUTPVOID*CompletionContext
);
所有的预操作回调都返回一个FLT_PRE_OPERATION_CALLBACK_STATUS.这个变量是如下定义的:
FLT_PREOP_SUCCESS_WITH_CALLBACK:
这个操作成功了而且微过滤器需要后操作回调。
FLT_PREOP_SUCCESS_NO_CALLBACK:
操作成功了,但是不需要后操作回调。
FLT_PREOP_PENDING:
微过滤器将在未来某个时候结束这个操作(通过调用FltCompletePendedOperation())。
微过滤器在返回这个值之前不需要做其他特殊的操作(比如IoMarkIrpPending())。
如果这个状态返回了,这个I/O操作被过滤管理器挂起(栈中的下层驱动都不会收到预操作回调),直到FltCompletePendedPreOperation()被调用。
FL
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 过滤器 驱动 开发 指南