下面的话摘自《Filter Driver Developer Guide》:
Certain minifilters need to swap the suppliedbuffer for certain operations. Consider a minifilter that implements customencryption. On a non-cached IRP_MJ_READ, it normally wishes to decrypt thecontents of the buffer that was read from the filesystem. Similarly on a write,it wishes to encrypt the contents. Take the latter case: the contents cannot beencrypted in place, because for IRP_MJ_WRITE, the maximal access to the bufferthat the minifilter can assume is IoReadAccess.
Hence the minifilter needs to supplant its ownbuffer which has read/write access, encrypt the contents of the original bufferinto the new one, and send the I/O down.
For this scenario, the Filter Manager supportsbuffer-switching. However there are a few rules to which the minifilter mustadhere:
1. Minifilters that switch a buffer must supply a post-callback. Thisis so that the buffer can be switched back by Filter Manager automatically.
2. If the minifilter is switching the buffer for an operation for whichFLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag was set, it MUST ensure that the new buffer is from non-pagedmemory (i.e. either from non-paged pool or from locked down memory).
3. If the above flag was not set, minifilter must adhere to therequirements of the device (by looking at the DeviceObjectFlags etc. in thevolume properties) when supplanting the buffer, based on the operation. Forinstance if it supports direct I/O, it must supply a MDL etc.
4. If the minifilter supplants a non-paged pool buffer for an operationfor which the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is not set, it must also build a MDL via MmBuildMdlForNonPagedPool() and supply it in the MdlAddress field. This is so that afilter/filesystem below will not attempt to lock non-paged pool (which canassert on checked Windows builds, and is also not good from a performanceperspective). If a MDL is supplied,filters/file systems will always access the buffer through the MDL (byobtaining a system address for it).
5. When switching a buffer, the minifilter should also switch the MDL(i.e. the buffer and the MDL must be in sync). It is fine to leave the MDL NULLsubject to the usual direct I/O exceptions.
6. Minifilter should NOT free the old buffer/old MDL.
7. Minifilter should NOT attempt to switch back the old buffer /MDL inits post-callback. Filter Manager does this automatically. In fact thebuffer/MDL the minifilter sees in the Iopb in its post-callback are theoriginal ones. Minifilters can remember the switched buffer by passing it invia their completion context.
8. Minifilters are expected to free the buffer they allocated (andsupplanted) in the post-callback. Filter Manager however, will automaticallyfree the MDL for the swapped buffer if any.
9. Minifilters that do not want Filter Manager to automatically freethe MDL for the swapped buffer can call FltRetainSwappedBufferMdl().
10. Minifilters that wish to access the swapped buffer’s MDL can use FltGetSwappedBufferMdl() in the post-callback. Since a filter/filesystem below theminifilter that swapped the new buffer in, may potentially create a MDL for it,Filter Manager saves any such MDL for the swapped buffer before calling thepost-callback for the minifilter that swapped the buffers. This API can be usedto access the MDL in that case.
楚狂人楚大侠在《Windows文件系统过滤驱动开发教程(第二版)》中对这部分内容做了翻译,如下:
一些微过滤器为了某些操作必须交换缓冲。考虑一个微过滤器实现加密算法,对一个非缓冲(non-cached)IRP_MJ_READ,它一般会希望把缓冲中的数据解密。同样的在写的时候,它希望把内容加密。考虑以下情况:内容无法在这个空间中加密。因为对于IRP_MJ_WRITE,这个微过滤器可能只有IoreadAccess权限。
因此微过滤器必须以他自己的有读写权限的缓冲区取代原来的缓冲区。加密了原缓冲区中的内容后写入新缓冲区后,再继续传递I/O请求。
为此,过滤管理器支持缓冲转换。有以下一些游戏规则必须遵守:
1. 改变了缓冲区的微过滤器必须有对应的后操作回调。这样缓冲能被过滤管理器自动的转换回来。
2. 如果改变的是一个标记有FLTFL_CALLBACK_DATA_SYSTEM_BUFFER标记的缓冲,必须保证新的缓冲是非分页内存。 (也就是比如来自非分页内存池或者锁定的内存) 。
3. 如果以上的标记没有设置,那么微过滤器必须按设备对象的要求来确定缓冲类型(可以在卷属性中查看DeviceObjectFlags等标记)。比如如果是支持直接I/O的,那么必须提供MDL等等。
4. 如果微过滤器使用非分页池中的缓冲来给一个没有设置FLTFL_CALLBACK_DATA_SYSTEM_BUFFER的操作,那么它也必须用MuBuildMdlForNonpagedPool()并把地址填写到MdlAddress域中。这是因为这么一来,下面的任何过滤器或者文件系统都不用再尝试去锁定非分页池(可以在构建的时候使用断言,但是对效率不利) ,如果提供了一个MDL,过滤器和文件系统总是可以通过MDL访问缓冲(可以获得一个系统内存地址来访问它) 。
5. 替换一个缓冲的时候,微过滤器也必须换掉MDL(就是说缓冲和MDL要保持同步了)。对于通常的直接I/O异常,可以把MDL留空。
6. 微过滤器不应该释放旧的MDL和缓冲空间。
7. 不要尝试在后操作回调中替换掉旧的缓冲和MDL.过滤管理器自动执行这些操作。 实际上微过滤器在后操作回调中的Iopb中见到缓冲空间和MDL是旧的(译者注:替换前的) 。微过滤器必须自己在上下文中记录新的缓冲区。
8. 微过滤器应该释放自己分配的(和替换过的)缓冲。无论如何,如果有的话,过滤管理器会自动释放新缓冲的MDL。
9. 微过滤器不希望过滤管理器自动释放交换过的缓冲MDL可以调用FltRetainSwappedBufferMdl()。
10. 过滤器如果希望访问交换过的缓冲的MDL可以在后操作回调中使用FltGetSwappedBufferMdl()。既然一个更下层的过滤器或文件系统交换了新的缓冲空间进来,那么有可能生成了一个MDL。在后操作回调中微过滤器交换缓冲之前,过滤管理器保存了所有这样的的MDL。这个调用可以用来访问这些MDL.