2008年9月12日星期五

文件过滤驱动中的重入处理收藏

While processing an IRP_MJ_CREATE a filter may need to open the file with different attributes/rights, etc. This is often done by using a second call to ZwCreatefile. This then will generate a call back into the FSD filter. Thus, a common filter issue is being able to detect this reentrancy.

在处理IRP_MJ_CREATE请求时,过滤驱动可能会使用不同的属性/权限等打开这个文件。这种情况经常发生在第二次调用ZwCreatefile时。这会导致生成一个对FSD 过滤驱动的回调(就是重入了).因而,正常的过滤驱动就要有能力检测这种重入的问题。

There are several ways of dealing with reentrancy during an IRP_MJ_CREATE operation, and the appropriate solution for your particular driver will depend upon the circumstances. In addition, there are a number of techniques that might work for a single file system filter driver, but that fail when used in a multi-filter environment.

在处理IRP_MJ_CREATE操作过程中,有几种方法可以处理重入,处理你的驱动(中的重入)的适当方法取决于你的环境。另外,有许多种技术可以在单个文件系统过滤驱动上工作,但在多层过滤的环境下可能会失效。

For Windows XP and newer versions of Windows, the best mechanism for opening a file within the filter is to use IoCreateFileSpecifyDeviceObjectHint. A filter driver can call this function and specify a given device object. The IRP_MJ_CREATE that is built will be passed to the specified device object. This technique avo
ids reentrancy issues and is the best mechanism available for a filter to open a file.

对Windows xp或者更新版的Windows来说,在过滤驱动中打开一个文件的最好方法是使用IoCreateFileSpecifyDeviceObjectHint.文件过滤驱动可以调用这个函数并且指定一个给定的设备对象。

For versions of Windows prior to Windows XP, this mechanism is not available. The best mechanism in this environment is to implement your own functional equivalent of IoCreateFileSpecifyDeviceObjectHint. This can be done by creating a second device object for each device you are filtering.

对Windows xp以前的Windows操作系统,这种方法无效。在这种环境下最好的方法是实现你自己的与IoCreateFileSpecifyDeviceObjectHint等价的功能。这可以通过给你要过滤的设备创建第二个设备对象来实现。

For example, suppose you decide to filter some given file system device object, FSDVolumeDeviceObject. You then create a device object MyFilterDeviceObject and attach it using IoAttachDeviceToDeviceStack (of course, in Windows XP you would use IoAttachDeviceToDeviceStackSafe instead). In addition, you create a second device object MyFilterShadowDeviceObject. This device object must be assigned a name ("DeviceMyFilterDevice27", for example). The name can be anything, but it must obviously be unique. In your device extension for your two device objects, you need to track this name, and you need to maintain pointers to the respective device objects (that is, the device extension for MyFilterShadowDeviceObject should point to MyFilterDeviceObject and the device object extension for MyFilterDeviceObject should point to yFilterShadowDeviceObject). Don't forget to set the StackSize field of the device object correctly!)

例如,假设你要过滤某个特定的文件系统设备对象,FSDVolumeDeviceObject(文件系统卷设备对象).这时你要创建一个设备对象 MyFilterDeviceObject 并且使用IoAttachDeviceToDeviceStack 函数(在windows xp下使用 IoAttachDeviceToDeviceStackSafe)来挂接它。另外,你还要创建第二个设备对象 yFilterShadowDeviceObject.这个设备对象必须被指定一个名字(例如"DeviceMyFilterDevice27",注: 这里指的第二个设备对象,即Shadow device object )。名字可以是任意的,但必须唯一的。在这两个设备的设备扩展结构中,你需要跟踪这个名字
(注,其实就是做标志,你要知道你当前是处在哪个设备 中,是第一个设备对象还是Shadow object )你需要维护一些指向相应的设备对象的指针(也就是说,MyFilterShadowDeviceObject的设备扩展要指向 MyFilterDeviceObject,MyFilterDeviceObject的设备扩展对象要指向 MyFilterShadowDeviceObject.不要忘了正确设置设备对象的StackSize成员变量。

Now, an IRP_MJ_CREATE request arrives in your filter, specifying MyFilterDeviceObject. To open the file without experiencing reentrancy problems, you call IoCreateFile (or ZwCreateFile). Since you must pass the name of the file being opened, you construct that by using both the name you gave MyFilterShadowDeviceObject and the name that is in the FileObject of the I/O stack Location (IoGetCurrentIr
pStackLocation(Irp)->FileObject).

现在,当IRP_MJ_CREATE 请求到达你的过滤驱动时,指定了 MyFilterDeviceObject.你调用IoCreateFile(或ZwCreateFile) 打开文件就没有重入的问题了.以后,你必须传递这个打开的文件的名字,这个名字是用你设置在MyFilterShadowDeviceObject中的名 字和从I/O 堆栈区域中得到的文件对象中的名字一起构造的。

Since you are passing a name in that points to your second device object, the I/O Manager will build the IRP_MJ_CREATE and pass the resulting I/O request packet to your driver, but specifying MyFilterShadowDeviceObject.

当你传递一个指向你的第二个设备对象的名字,I/O管理器会构建IRP_MJ_CREATE 并且传递I/O请求的结果到你的驱动,但指定了MyFilterShadowDeviceObject.

In your IRP_MJ_CREATE dispatch handler you must detect that this is a "shadow" device object, rather than a typical filter device object. In this case, you should send the IRP_MJ_CREATE operation down to the device being filtered by MyFilterDeviceObject. Indeed, since you should not need to do any further processing, you can use IoSkipCurrentIrpStackLocation (rather than IoCopyCurrentIrpStackLocationToNext).

在你的IRP_MJ_CREATE 分发例程处理函数中,你必须检测它是一个"Sahdow"设备对象而不是是一个典型的过滤设备对象。在这种情况下,你必须将IRP_MJ_CREATE操 作下传到已经被 MyFilterDeviceObject过滤了的设备中。确实,此后你不需要作进一步的处理,你可以用 IoSkipCurrentIrpStackLocation函数(不是IoCopyCurrentIrpStackLocationToNext).

The original filter (where the IoCreateFile call was made) will receive back a file handle that can then be used for subsequent operations (using the Zw API routines).

原始的过滤器(调用IoCreateFile的设备对象)会收到一个可以用于后续操作的文件句柄(使用Zw API 例程)。

Typically, filter drivers that attempt to use IoCreateFile or ZwCreateFile with the same file/device name as the original request experience reentrancy into their driver. A number of techniques for dealing with this have been tried in the past, but they exhibit various problems when combined with other filters. These i
nclude:

· Appending a "special string" to the end of the file name. This will not work when two filters using this technique are loaded onto the same system (since each one appends its "special string" onto the previous filter's "special string").

· Track thread identifiers to detect reentrancy. This technique fails when combined with a filter that utilizes a separate service for opening the file; filters sometimes must switch to a different thread context in order to eliminate stack overflow conditions.
· Building create IRPs within a filter. This technique does work properly, but is completely unsupported and quite difficult to implement correctly. Because of this, it is a fragile solution that should not be used given the availability of alternative mechanisms.
· Re-using the file object from the current IRP_MJ_CREATE. In this sequence, the filter allows the create operation to complete and then uses the file object subsequently. When done, the filter then sends a cleanup and close operation to the underlying file system. It then sends the original IRP_MJ_CREATE operation to the underlying FSD. There are several potential issues with this approach. First, in this technique the filter does not have a file handle for the file object and thus cannot use the Zw API calls. Second, the file object must be restored to its original state - otherwise, there are fields within it that are not set up properly. Third, because the file object has not yet been "properly referenced" the filter may find that the OS deletes the object because its reference count drops to zero during its use. Used carefully, this technique has been successful in the past.

通常,试图使用与原始的请求相同的文件/设备名来调用IoCreateFile或ZwCreateFile都会遇到重入。过去已经有很多种用于处理重入的技术被试验,但当它和其它过滤器协同工作时会表现出各种各样的问题。
这些技术包括:

1. 添加一个特殊的串到文件名尾部。当两个使用这种技术的过滤器被加载到相同的系统时会不能工作(此后,每个过滤器都会添加它的特殊串到先前的特殊串后面。

2. 跟踪线程ID来检测重入. 这种技术在过滤驱动使用一个单独的服务来打开文件时会失败。过滤器驱动有时必须切换到不同的线程上下文来消除堆栈溢出的情况。

3.在过滤器内部创建IRP请求。这种技术会正常工作,但它完全没有支持并且很难正确地实现。因此,这是种脆弱的方案,在有可选择的机制的情况下不会被使用。

4 重用当前IRP_MJ_CREATE请求的文件对象.在这种情况下,过滤驱动允许创建(文件)的操作完成,并且可以在随后使用这个文件对象。在操作完成 后,过滤驱动会向下层文件系统发送Cleanup和Close操作。它接着会发送原始的IRP_MJ_CREATE操作到下层的FSD.这种方法有几个潜 在的问题。首先,在这种技术中过滤器驱动没有文件对象的句柄,因而不能使用Zw API调用。其实,文件对象必须存贮它原始的状态,否则在它内部的一些数据成员就没有被正确设置.最后,因为文件对象还没有被"正确地引用",过滤驱动会 发现操作系统删除这个对象,因为它的引用计数在它的使用过程中被减到0.小心地使用,这种技术在过去有成功的案例。

Regardless of the technique used, the filter driver must be cognizant of the issues involving oplocks (and the FILE_COMPLETE_IF_OPLOCKED option). If an oplock break occurs during an IRP_MJ_CREATE, whether from an original application, or from a filter driver attempting to open the file, the filter must be able to handle it gracefully.

不管使用哪种技术,过滤驱动必须能识别陷于OpLocks的问题(FILE_COMPLETE_IF_OPLOCKED 选项)。在一个IRP_MJ_CREATE中发生oplock 打断,不论是从原始的应用程序还是过滤驱动中的打开文件操作,过滤驱动必须能够温和地处理它.

Finally, the filter must be aware of the fact that even though it is calling from kernel mode, the underlying FSD will continue to enforce sharing access against the file.

最后,过滤驱动必须能知道这种情况:虽然调用来自内核模式,下层的FSD会对这个文件强制进行共享存取。

没有评论: