阅读:2690回复:12
FSFD的引用计数
Tracking State and Context - Reference Counting for File System Filter Drivers The NT Insider, Vol 9, Issue 2, Mar-Apr 2002 | Published: 15-Apr-02| Modified: 06-May-03
声明:原文来自www.osr.com,所有权利归原作者所有,翻译并贴在驱动开发网的目的只为学习和交流,除了商用你可以随意地使用这篇译文。但请不要删除声明。 ——by jununfly 版本:20090112 Windows NT,2000或XP环境中,文件系统过滤驱动的一项挑战就是要知道什么时候它正好能抛弃每个文件的状态信息。虽然对文件系统过滤驱动来说追踪像这样基于FILE_OBJECT中的FsContext域的状态是个惯例,但新手在发现错过了一些I/O操作时可能会感到很失落。 现在分析这个问题,观察到IRP_MJ_CREATE 操作和IRP_MJ_CLOSE 操作的成对/*原文:匹配*/出现很容易但不足以决定什么时候可以抛弃过滤驱动中FsContext 相关的文件状态。之前我们已经描述过为什么会出现这样的情况(文件系统对流文件对象的使用),但本文的重点是描述一个允许过滤驱动在文件的最后一次引用真正地死去之前不会抛弃文件状态的运算法则。 首先,回顾一下为什么引用计数对文件系统过滤驱动来说不是一个毫无意义的练习,然后我们将回顾任何运算法则必须要考虑的各种情况,并最终设计通常由文件系统过滤驱动维护的用来跟踪对每个文件数据的引用的运算法则。 文件系统有多种创建文件对象的机制可能用于内部处理。这些包括IoCreateStreamFileObject, IoCreateStreamFileObjectLite,和 IoCreateStreamFileObjectEx.在所有的版本中这些并不一定都能用,但刚好Windows NT 4.0 IFS Kit包括IoCreateStreamFileObject调用。Windows XP IFS Kit中说明了IoCreateStreamFileObject和IoCreateStreamFileObjectLite,它们提供相似的功能– 它们基于一个现有的文件对象或设备对象创建文件对象。IFS Kit说明文件在 IoCreateStreamFileObjectLite 那页里面概括了文件系统过滤驱动的问题: “因此过滤驱动应该期望收到先前没见过的文件对象的IRP_MJ_CLOSE请求。” 当然,文件系统过滤驱动接收到文件对象的IRP_MJ_CLOSE操作并不意味着过滤驱动复杂化了引用计数。 在考虑事情的先后顺序中,我们注意到我们关心的是普通用户访问的IRP_MJ_CREATE和IRP_MJ_CLOSE的交迭以及文件系统访问的IRP_MJ_CLOSE。因此,我们可能观测到以下情节: 一个应用程序打开文件然后关闭它;过滤器从未观测到用于给定文件的任何流文件对象。 过滤器仅仅观测到流文件对象而没有观测到任何对一个文件的应用访问。 应用打开文件而过滤器观测到应用和用于访问目标文件的流文件对象。 过滤器观测到流文件对象的使用,然后观测到一个应用程序打开这个文件。 无论文件系统过滤驱动使用了什么算法来跟踪其上下文和状态信息,它必须能解决以上所有的情节。 在分析跟踪信息的各种可用的算法中,我们注意到存在一些情况能使用到的技术简单化。因此我们在这里描述两个独立的算法– 一种对大多数情形的状态跟踪都有用,第二种在文件系统过滤驱动只关心数据操作时有用。 对于一个必须跟踪一个文件对象的所有实例的文件系统过滤驱动来说,它不仅需要跟踪引用技术,而且要跟踪它已经观测到的指定的文件对象和IRP_MJ_CREATE。这样的一个过滤器必须维护一个引用计数(为了经由IRP_MJ_CREATE机制看到的文件对象)和包含被此过滤器看到的流文件对象的一个明晰的列表(或者其他为了跟踪这些文件对象的数据结构)。因此,在每一个I/O操作上,过滤器要检查FILE_OBJECT的Flags 域中的FO_STREAM_FILE 位。如果设置了这个位,过滤器检验此文件对象是否在它的列表中 – 如果没有,就分配必要的存储空间来存放对这个文件对象的跟踪信息并把它插入到它的列表中。然后,当它观测到一个IRP_MJ_CLOSE操作,它会消耗引用计数或在合适时从列表中移除它。仅当列表为空且引用计数为零时过滤器删除跟踪数据结构。 因此,当一个文件对象对过滤驱动来说可能完全是内部地被创建或由于应用程序请求打开文件而被创建时,过滤器必须在它观测到它还没有跟踪其信息的一个IRP_MJ_CREATE或一个流文件对象时创建每文件的(per-file/*可以理解为每个文件都是这样的*/)跟踪信息。过滤驱动必须仍然要设法决定合适的关于追踪此类文件的操作,而这是比较困难的。在我们后面要描述的”简单的”算法中,我们将提出一项在适当长的时间中保存上下文来允许过滤器避免再次评估(对跟踪给定文件的必要性的)的技术。 这个算法考虑到了我们之前描述过的四种截然不同的情节: 如果应用程序首次打开这个文件然后关闭它,而没有介入流文件对象的使用,这个算法会因为要在引用计数减为零时删除文件上下文而起总用。因此没有流上下文,所以列表为空。 如果仅仅观测到流文件对象,过滤驱动可能只看见这个文件对象的一个 IRP_MJ_CLOSE 操作,此时这个文件无须关注太多。否则,在观测到使用流文件对象的首个I/O操作的基础上,过滤器将创建一个上下文记录并插入给定文件对象的跟踪信息。当它观测到后来的IRP_MJ_CLOSE操作时,它将删除列表中相应的入口并删除文件上下文块。 如果一个应用最先打开这个文件,过滤驱动将在那时创建每流的(per-stream)文件上下文信息。如果它随后观测到一个使用流文件对象的I/O操作,且在列表中找不到,则创建一个新的入口。然后,不管IRP_MJ_CLOSE操作以什么顺序到达过滤驱动中,在引用计数为零且流文件对象列表为空之前它都不会删除每文件的上下文。 如果首次观测到流文件对象,过滤驱动会在此时创建每文件的上下文信息并把此流文件对象的一个跟踪块插入到列表中。一个后来的应用打开这个文件会使过滤器增加引用计数。在引用计数为零且流文件对象列表为空之前过滤器都会继续维护每文件的上下文信息。 当然,存在基于这些基本情节的众多变化,包括多重流文件对象和多重应用打开。然而,我们已经集中了临界情形,因为我们维护额外的流文件对象或应用打开将以可比较的形式工作,虽然有额外的入口和引用。 图1 — Section Object/File Object的关系 许多过滤驱动能用一个更简单的(更低消耗的)算法,这是因为它们仅关心用户文件的数据操作。这种情况下,我们注意到唯一的问题是当流文件对象被用于支持内存管理器的一个部分对象时。 这是因为内存管理器指定的任何分页的I/O操作都基于用给定部分对象引用的文件对象。(对过滤驱动的)风险是它会观测到以下顺序: 应用打开文件; 文件系统创建一个流文件对象,然后这个对象被作为文件对象来支持部分(例如,用流文件对象来调用CcInitializeCacheMap)。 应用关闭文件。因为没有未决的引用(除了应用句柄) IRP_MJ_CLEANUP和IRP_MJ_CLOSE被发送给文件系统(因此通过了过滤器)。过滤驱动删除它的每文件的上下文信息。 内存管理器用分页I/O操作flush数据的一切改变(或许通过部分, 当为内存映射的文件的访问时)到文件系统。 这个情节的问题是因为过滤器已经删除了它的每文件的上下文信息,而后来的写操作因为看起来无关而被过滤器错过。当然,使用我们之前描述的算法,基于观测到新的分页I/O操作,过滤器将(再次)创建每文件的上下文信息,然后首先探知文件是否已经被跟踪了。当这个方法行得通时,我们能用一次额外的检查简化这种情况下的处理– 尤其,在删除每文件的上下文之前,文件对象中的部分对象指针必须都为NULL。否则,我们知道存在一些文件对象支持这个部分,而我们的过滤驱动从来没有观测到它。 因此,对一个关心所有的I/O操作和追踪所有的文件状态的过滤驱动来说,当这依然有效时这能被添加到之前用于保存状态信息的算法中。 对于只关心数据操作的过滤驱动来说(读写)这提供了一个更简单的算法: 对任何一个IRP_MJ_CREATE,过滤驱动增加每文件的上下文结构的引用计数。 对任何一个IRP_MJ_CLOSE,如果文件对象的FO_STREAM_FILE位没有被设置且CcGetFileObjectFromSectionPtrs不会返回这个文件对象,过滤驱动消耗每文件的上下文结构上的引用计数。 如果每文件的上下文结构的引用计数为零且FILE_OBJECT的SectionObjectPointers域的ImageSectionObject和DataSectionObject 都为零,那么过滤驱动可能删除每文件的上下文数据。 这个算法避免了跟踪单独的文件对象的需要。它忽略了一切没有涉及分页I/O的流文件对象。对于多数过滤驱动来说,这个解决方案足够它们跟踪它们的每文件的状态。 这起作用是因为引用计数表示文件的用户级打开。仅当一个用户级文件对象被关闭时(文件对象没有FO_STREAM_FILE位证明了这点)引用计数才会被消耗,因此当引用计数为零时就没有对文件的用户引用。类似的,部分对象表示文件的VM状态。如果两个部分对象指针都为NULL则没有对文件的VM引用– 因此,没有文件的内存映射(无论是被应用还是缓存管理器)。既然过滤器只关注数据操作,这就足够了。任何新的数据操作都会需要文件被重打开(re-opened)。一定要注意这个算法仅在你的过滤器观测到文件的所有操作时起作用 – 如果你动态地加载你的过滤驱动而文件已经在那之前被打开,这个算法就不能满足。 对仅在Windows XP 或更新版本的操作系统上运行的文件系统过滤驱动来说,没有必要以这种形式来跟踪每文件的上下文信息。取而代之,文件系统过滤驱动能使用例程FsRtlInsertPerStreamContext和FsRtlRemovePerStreamContext 来允许文件系统运行时间库包来管理每流文件上下文。对支持Windows XP 及更早版本的操作系统的过滤驱动来说,这两种计数能被用来实现一个平台无关的公有的机制。 |
|
|
沙发#
发布于:2009-01-12 13:57
更希望出现的是交流,不是一个人做好了饭还得教人一口一口怎么把这饭吃下去,怎么吃才有滋味。。。
|
|
板凳#
发布于:2009-01-12 14:19
更好的办法应该是基于每个流的上下文的跟踪,这在filtmgr中已经使用了
|
|
|
地板#
发布于:2009-01-12 16:47
我发现一个奇怪的现象,明明我觉着很有用的网页才翻出来放在这里让大家看的,貌似只有已经不用看的大牛们扫下,难道题目太不吸引人?
|
|
地下室#
发布于:2009-01-14 11:30
我发现某些变态的安全类程序会把StreamContext全部清除掉,导致采用StreamContext机制的驱动无法正常工作,真XXX。
|
|
5楼#
发布于:2009-01-14 11:37
楼上说的是什么变态的程序啊?
|
|
|
6楼#
发布于:2009-01-14 11:39
引用第4楼AlexSho于2009-01-14 11:30发表的 : 不会吧!那国内的好多采用mini的加密软件 不是全部完蛋了? |
|
7楼#
发布于:2009-01-14 12:56
我对这些变态的安全类程序也很感兴趣不知是哪些。貌似是因为对NTFS多重流机制的众多质疑声孕育出了这么个"安全措施"。我看过几篇院士之类的学院派文章,无一例外都是说流是巨大的安全隐患。无论怎样,光明与黑暗本就背靠背,哪面朝向你就看你怎么行动。如果因噎废食,未免得不偿失。
|
|
8楼#
发布于:2009-01-19 15:45
思路是好的,但概念辨析的不是足够细。文件系统内的引用计数,不是1个,而是4个。
|
|
|
9楼#
发布于:2009-01-20 09:36
这篇只是翻译,楼上的可以对其中概念不明细的地方附加点阐述,或者说说自己的看法
|
|
10楼#
发布于:2009-01-21 12:52
看得非常累。翻译后应该把句子理顺下
估计不少初学Filter的人还没有注意到这个现象吧~所以少有人看 Stream文件对象先走CLEANUP对记数影响很大,当初在没考虑到这个问题时犯了不少错误。 虽然现在解决了,还是非常感谢楼主的热心。~ |
|
11楼#
发布于:2009-01-21 13:02
嘿嘿,看时顺手翻译没有后加工处理
|
|
12楼#
发布于:2010-03-25 11:56
都是大牛在看,我是牛痘,我也来看看。
|
|