写Callback例程的Pre-oper和Post-oper
一个minifilter驱动可以在它的DriverEntry例程中为它需要过滤的任何类型的I/O操作注册至多一个pre-oper callback例程和至多一个post-oper callback例程.
Minifilter可以选择要过滤哪种类型的I/O. minifilter驱动可以为一个给定类型的I/O操作只注册一个pre-oper callback 例程,也可只注册一个post-oper callback例程. Minifilter驱动只会收到那些它已经为之注册了一个pre-oper或post-oper callback例程的I/O操作.
pre-oper callback例程和legacy过滤驱动模型中的分发例程类似.当filter管理器处理一个I/O操作时,它会调用已经为这种类型的I/O操作注册过的minifilter驱动实例栈中的每个minifilter驱动的pre-oper callback例程. 栈中顶端的minifilter驱动—就是其实例拥有最高altitude的那个—会首先收到这个操作. 当该 minifilter驱动结束了对这个操作的处理时,它会把此操作返回给filter管理器,然后filter管理器会把操作传给下一个最高的(除了刚刚处理过这个操作的那个之外最高的)minifilter驱动,以此类推. 当 minifilter驱动实例栈中的所有minifilter驱动已经处理过了这个I/O操作—除非某个minifilter驱动已经完成了这个I/O操作— filter管理器发送此操作给legacy过滤器和文件系统.
post-oper callback例程与legacy过滤驱动模型中的完成例程类似.当I/O管理器把操作传给已经为某个I/O操作注册了完成例程的文件系统和legacy过滤器时,该操作的完成处理就开始了. 在这些完成例程结束之后,filter管理器会执行对此操作的完成处理. 接着filter管理器会调用已经为这种类型的I/O操作注册过的minifilter驱动实例栈中的每个minifilter驱动的post-oper callback例程.栈中的底部minifilter驱动—就是其实例拥有最低 altitude的那个—会首先收到这个操作.当该minifilter驱动结束对这个操作的处理时,它把操作返回给filter管理器,然后filter管理器会把操作传给下一个最低的minifilter驱动,以此类推.
本节包括:
一、注册Pre-oper和Post-oper Callback例程
要注册pre-oper callback例程和 post-oper callback例程,minifilter驱动只需在其DriverEntry例程中调用FltRegisterFilter.minifilter驱动要把一个FLT_REGISTRATION结构的指针传给该函数的 Registration参数. 这个结构的成员OperationRegistration包含一组FLT_OPERATION_REGISTRATION 结构的指针, 其中之一是minifilter驱动必须过滤的.
此指针组中的每一个FLT_OPERATION_REGISTRATION结构,除了最后一个, 都包含以下信息:
操作的主函数代码
对read和write操作(IRP_MJ_READ和IRP_MJ_WRITE)来说,有一套用于基于IRP的I/O操作的标记,它们指定了是否要忽视cached I/O或分页I/O或两者都忽视
至多一个pre-oper callback例程和一个post-oper callback例程的入口点
此指针组的最后一个元素必须是{IRP_MJ_OPERATION_END}.
以下从Scanner minifilter驱动中节选出来的代码展示了一组 FLT_OPERATION_REGISTRATION结构.Scanner minifilter驱动为IRP_MJ_CREATE注册了pre-oper和post-oper callback例程,为IRP_MJ_CLEANUP和IRP_MJ_WRITE例子注册了pre-oper callback例程.
const FLT_OPERATION_REGISTRATION Callbacks[] = {
{IRP_MJ_CREATE,
0,
ScannerPreCreate,
ScannerPostCreate},
{IRP_MJ_CLEANUP,
0,
ScannerPreCleanup,
NULL},
{IRP_MJ_WRITE,
0,
ScannerPreWrite,
NULL},
{IRP_MJ_OPERATION_END}
};
二、在Minifilter驱动中过滤I/O操作
以下是几个在文件系统minifilter驱动中过滤指定类型的I/O操作的几个指导方针:
l IRP_MJ_CREATE的pre-oper callback例程不能为文件、流或流句柄查询或设置context,因为在pre-create时,计划要创建的文件或流还没有被确定.
l IRP_MJ_CLOSE的post-oper callback 例程不能为文件、流或流句柄查询context,因为相关的系统内部结构都在调用post-close例程之前被释放了.
l Minifilter驱动一定不能令IRP_MJ_CLEANUP 或IRP_MJ_CLOSE 操作失败. 这些操作可以被pend,被返回给filter管理器或以STATUS_SUCCESS被完成. 不过,一个pre-oper callback例程一定不能令这些操作失败.
l Minifilter驱动不可以为IRP_MJ_SHUTDOWN 注册一个post-oper callback例程.
三、写Pre-oper Callback例程
一个文件系统minifilter驱动用一个或多个pre-oper callback例程来过滤I/O操作. Pre-oper callback例程 与legacy FSFD中使用的分发例程类似.
Minifilter驱动通过存储callback例程的入口点到FLT_REGISTRATION结构的成员OperationRegistration 中来为某个特定类型的I/O操作注册一个pre-oper callback例程,此结构会被minifilter驱动在其DriverEntry例程中作为一个参数传给 FltRegisterFilter.
Minifilter驱动仅接收那些它们已经为之注册了一个pre-oper或post-oper callback例程的I/O操作.minifilter驱动可以只注册一个pre-oper callback例程或只注册一个post-oper callback例程.
pre-oper callback例程的定义如下:
typedef FLT_PREOP_CALLBACK_STATUS
(*PFLT_PRE_OPERATION_CALLBACK) (
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
OUT PVOID *CompletionContext
);
像一个分发例程一样,一个pre-oper callback例程可以在IRQL = PASSIVE_LEVEL 或在IRQL = APC_LEVEL被调用.一般它在发起I/O请求的线程的context中在IRQL = PASSIVE_LEVEL被调用.对fast I/O和FsFilter操作来说,pre-oper callback例程总在IRQL = PASSIVE_LEVEL被调用.不过,对一个基于IRP的操作来说,如果一个更高层的过滤器或minifilter驱动pend此操作来让用worker线程做处理的话,minifilter驱动的pre-oper callback例程可以在一个系统worker线程的context中被调用.
当filter管理器为一个给定的I/O操作调用某个minifilter驱动的pre-oper callback例程时,该minifilter驱动临时地控制此I/O操作.minifilter驱动在它做完以下事情之前会保持其控制权:
从pre-oper callback 例程中返回一个除了FLT_PREOP_PENDING之外的状态值.
从一个已经处理了在pre-oper callback例程中被pend的操作的工作例程中调用FltCompletePendedPre-oper.
这节包括:
1.在minifilter实例栈上向下传I/O操作
当某个minifilter驱动的pre-oper callback例程 或工作例程返回一个I/O操作给filter管理器时, filter管理器把该操作发送给minifilter驱动实例栈中在当前minifilter驱动之下的minifilter驱动,或给legacy过滤器和文件系统来做进一步处理.
pre-oper callback例程通过返回下列状态值之一来返回I/O操作给filter管理器进行深入处理:
FLT_PREOP_SUCCESS_NO_CALLBACK (所有操作类型)
FLT_PREOP_SUCCESS_WITH_CALLBACK (所有操作类型)
FLT_PREOP_SYNCHRONIZE (仅基于IRP的I/O)
注意 尽管FLT_PREOP_SYNCHRONIZE只针对基于IRP的I/O操作,你也可以为其他操作类型返回这个值.如果是为了非基于IRP的I/O操作返回此值,则filter管理器会把它当做FLT_PREOP_SUCCESS_WITH_CALLBACK来处理.
在pre-oper callback例程中被pend的操作的工作例程,当它调用FltCompletePendedPre-oper来恢复对被pend的I/O的处理时filter管理器通过在CallbackStatus参数中传递上述状态值之一返回I/O操作给filter管理器.
本节包括:
1) Returning FLT_PREOP_SUCCESS_WITH_CALLBACK
如果某个minifilter驱动的pre-oper callback例程 返回了FLT_PREOP_SUCCESS_WITH_CALLBACK, filter管理器就会在I/O完成期间调用它的post-oper callback例程.
注意 如果minifilter驱动的pre-oper callback例程返回了 FLT_PREOP_SUCCESS_WITH_CALLBACK但该驱动还没有为操作注册一个post-oper callback例程,那么系统会在checked build上assert.
如果这个minifilter驱动的pre-oper callback例程返回了 FLT_PREOP_SUCCESS_WITH_CALLBACK, 那它就可以在其输出参数CompletionContext中返回一个非NULL的值. 该参数是一个可选的context指针,它被传给相应的post-oper callback例程.此例程会在其 CompletionContext 入参中收到这个指针.
对所有类型的I/O操作都可以返回FLT_PREOP_SUCCESS_WITH_CALLBACK状态值.
2) Returning FLT_PREOP_SUCCESS_NO_CALLBACK
如果某个minifilter驱动的pre-oper callback例程 返回了 FLT_PREOP_SUCCESS_NO_CALLBACK, 那么在I/O操作期间filter管理器就不能调用它的 post-oper callback例程。
如果这个minifilter驱动的pre-oper callback例程返回了FLT_PREOP_SUCCESS_NO_CALLBACK, 那么它必须在它的出参CompletionContext 返回NULL.
对所有类型的I/O操作都可以返回FLT_PREOP_SUCCESS_NO_CALLBACK状态值.
3) Returning FLT_PREOP_SYNCHRONIZE
如果某个minifilter驱动的pre-oper callback例程 通过返回FLT_PREOP_SYNCHRONIZE 来同步一个I/O操作,那么filter管理器就会在I/O完成期间调用它的post-oper callback例程.
Filter管理器像调用pre-oper callback一样在同一线程的上下文中在 IRQL <= APC_LEVEL调用这个minifilter驱动的post-oper callback例程. (注意这个线程上下文不一定是发起线程上下文.)
注意 如果minifilter驱动的pre-oper callback例程返回了 FLT_PREOP_SYNCHRONIZE, 但它还没有为操作注册一个 post-oper callback例程,系统会在checked build上assert.
若minifilter驱动pre-oper callback例程返回FLT_PREOP_SYNCHRONIZE, 那么它就可以在其出参CompletionContext中返回一个非NULL值. 此参数是一个可选的context指针,它被传给相应的post-oper callback例程.该例程会在其入参CompletionContext 中收到这个指针.
Minifilter驱动的pre-oper callback例程应只对IRP-I/O操作返回FLT_PREOP_SYNCHRONIZE.不过,此状态值可以作为其他操作类型的返回值. 如果它作为返回值是不是IRP-I/O操作,filter管理器会把这个值当FLT_PREOP_SUCCESS_WITH_CALLBACK来处理.
用FLT_IS_IRP_OPERATION宏可以确定一个操作是不是一个IRP-I/O操作.
Minifilter驱动不应为create操作返回FLT_PREOP_SYNCHRONIZE, 因为这些操作已经被filter管理器同步了. 如果一个minifilter驱动已经注册了 IRP_MJ_CREATE 操作的pre-oper和post-oper例程,则这两个例程都会在IRQL = PASSIVE_LEVEL在同一线程上下文中被调用.
Minifilter不允许为异步读写操作返回FLT_PREOP_SYNCHRONIZE. 这么做严重地降低minifilter驱动和系统的性能,甚至引起死锁,例如,已修改的页writer thread会被阻塞.在为IRP的读写返回FLT_PREOP_SYNCHRONIZE之前,minifilter驱动应调用FltIsOperationSynchronous来校验操作是否同步.
以下类型的I/O操作不能被同步:
· Oplock文件系统控制(FSCTL)操作(MajorFunction为 IRP_MJ_FILE_SYSTEM_CONTROL; FsControlCode为 FSCTL_REQUEST_FILTER_OPLOCK, FSCTL_REQUEST_BATCH_OPLOCK, FSCTL_REQUEST_OPLOCK_LEVEL_1, 或FSCTL_REQUEST_OPLOCK_LEVEL_2.)
· 通知改变目录操作(MajorFunction为IRP_MJ_DIRECTORY_CONTROL; MinorFunction为 IRP_MN_NOTIFY_CHANGE_DIRECTORY.)
· Byte-range锁请求(MajorFunction为IRP_MJ_LOCK_CONTROL; MinorFunction为IRP_MN_LOCK.)
FLT_PREOP_SYNCHRONIZE不能作为以上任何操作的返回值。
2.在Pre-oper Callback例程中完成一个I/O操作
要完成一个I/O操作意味着你得停止该操作的处理,给它指定一个最终的NTSTATUS值并把它返回给filter manager.
当某个minifilter驱动完成了一个I/O操作时,filter管理器做以下这些事情:
· 不再把此操作发送给当前minifilter驱动之下的minifilter驱动、legacy过滤器以及文件系统.
· 调用minifilter驱动实例栈中在当前minifilter驱动之上的minifilter驱动的post-oper callback例程.
· 不再调用当前minifilter驱动对此操作的post-oper callback例程(如果存在一个的话).
Minifilter驱动的pre-oper callback例程 通过执行以下这些步骤完成一个I/O操作:
1. 设置callback数据结构的IoStatus.Status域为此操作最终的NTSTATUS值.
2. 返回FLT_PREOP_COMPLETE.
一个完成一个I/O操作的pre-oper callback例程不可以设置一个非NULL的完成context (在出参CompletionContext 中).
Minifilter驱动也可以在一个之前被pend的I/O操作的工作例程中完成一个操作,它要执行的步骤如下:
1. 设置callback数据结构的IoStatus.Status域为此操作最终的NTSTATUS值.
2. 当工作例程调用FltCompletePendedPost-oper时,在参数CallbackStatus中传递FLT_PREOP_COMPLETE.
当完成一个I/O操作时,minifilter驱动必须设置callback数据结构的 IoStatus.Status域为此操作最终的NTSTATUS值,但是这个NTSTATUS值不可以是STATUS_PENDING或STATUS_FLT_DISALLOW_FAST_IO.对于一个 cleanup或close操作来说,这个域必须是STATUS_SUCCESS.这些操作不能以其他任何NTSTATUS值完成.
完成一个I/O操作常常牵涉依赖于NTSTATUS值令该操作成功或失败的操作:
· 要令I/O操作成功意味着以一个成功或如STATUS_SUCCESS值这样的信息化的NTSTATUS值完成它.
· 要令I/O操作失败意味着以一个错误或像STATUS_INVALID_DEVICE_REQUEST或者 STATUS_BUFFER_OVERFLOW这样的警告NTSTATUS值完成它.
NTSTATUS值的定义在ntstatus.h中.这些值分为四类:成功,信息化的, 警告和错误. 更多这些值的信息可以参考Using NTSTATUS Values一文.
3.在Pre-oper Callback例程中拒绝一个Fast I/O操作
在某些情况下,minifilter驱动可能会选择拒绝一个fast I/O操作而非完成它.这会阻止该操作使用fast I/O.
和完成I/O操作一样,拒绝fast I/O操作意味着停止在它上的处理并把它返回给filter管理器。不过,拒绝和完成不同。如果某个minifilter驱动拒绝一个由I/O管理器发出的fast I/O,I/O管理器可能会以等价的基于IRP的操作重发同一操作。
当某个minifilter驱动的pre-oper callback例程 拒绝了一个fast I/O操作时,filter管理器会做以下这些:
· 不再发送操作给在当前minifilter驱动之下的minifilter驱动、legacy过滤器以及文件系统。
· 调用minifilter驱动实例栈中处于当前minifilter驱动之上的minifilter驱动的post-oper callback例程.
· 不再带哦用当前minifilter驱动用于此操作的post-oper callback例程,如果有一个的话.
Minifilter驱动通过从操作的pre-oper callback例程中返回FLT_PREOP_DISALLOW_FASTIO来拒绝一个fast I/O操作.
Pre-oper callback例程不应设置callback数据结构的IoStatus.Status域,因为filter管理器会自动设置此域为STATUS_FLT_DISALLOW_FAST_IO.
FLT_PREOP_DISALLOW_FASTIO仅可作为fast I/O操作的返回值.要决定一个操作是否为一个fast I/O操作,参考FLT_IS_FASTIO_OPERATION.
Minifilter驱动不可为IRP_MJ_SHUTDOWN,IRP_MJ_VOLUME_MOUNT或IRP_MJ_VOLUME_DISMOUNT操作返回FLT_PREOP_DISALLOW_FASTIO.
4.在Pre-oper Callback例程中Pend一个I/O操作
Minifilter驱动的pre-oper callback例程 可以通过把操作发到一个系统工作队列并返回FLT_PREOP_PENDING来pend一个I/O操作.返回这个状态值表示minifilter驱动将保留I/O操作的控制直到它调用 FltCompletePendedPreOperation来恢复对I/O操作的处理.
Minifilter驱动的pre-oper callback例程通过执行以下步骤来pend一个I/O操作:
1. 通过调用像FltQueueDeferredIoWorkItem这样的一个例程来发送I/O操作给一个系统工作队列.
2. 返回FLT_PREOP_PENDING.
必须pend所有(或大多数)引入的I/O操作的Minifilter驱动不应使用像 FltQueueDeferredIoWorkItem这样的例程来pend操作,因为调用此例程会引起系统工作队列溢出.换句话说,这样的minifilter驱动应使用一个安全取消队列.更多关于使用安全取消队列的信息参考FltCbdqInitialize.
注意如果以下条件为真则对FltQueueDeferredIoWorkItem的调用将会失败:
· 操作不是IRP-I/O操作.
· 操作是一个分页I/O操作.
· 当前线程的TopLevelIrp域不为NULL. (更多关于如何找出此域的值的信息参考IoGetTopLevelIrp.)
· I/O操作的目标实例正被拆卸.
如果minifilter驱动的pre-oper callback 例程返回FLT_PREOP_PENDING, 则它必须在出参CompletionContext 中返回NULL.
Minifilter驱动只可为IRP-I/O操作返回FLT_PREOP_PENDING .要决定一个操作是否为IRP-I/O操作,使用FLT_IS_IRP_OPERATION宏.
出列和处理I/O操作的工作例程必须调用 FltCompletePendedPreOperation来恢复对操作的处理.
四、写Post-oper Callback例程
文件系统minifilter驱动用一个或多个post-opera callback例程来过滤I/O操作.
Post-oper callback例程 与legacy FSFD中使用的完成例程类似.
Minifilter驱动为某个特定类型的I/O操作注册一个post-oper callback例程的方法与它为之注册一个pre-oper callback例程的方法相同—就是说,都是通过把callback例程的入口点存储到FLT_REGISTRATION结构(它将作为参数被传给DriverEntry例程中FltRegisterFilter)的OperationRegistration 成员来实现的.
Minifilter仅接收它们已经为之注册了一个pre-oper或post-oper callback例程的I/O操作.minifilter驱动可以为一个给定类型的I/O操作只注册一个pre-oper或只注册一个post-oper callback例程.
Post-oper callback例程的定义如下:
typedef FLT_POSTOP_CALLBACK_STATUS
(*PFLT_POST_OPERATION_CALLBACK) (
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
IN PVOID CompletionContext,
IN FLT_POST_OPERATION_FLAGS Flags
);
和完成例程一样,post-oper callback例程在IRQL <= DISPATCH_LEVEL及任意线程的上下文中被调用.
因为它在DISPATCH_LEVEL被调用,所以它不能调用必须在更低 IRQL调用的内核模式例程,如FltLockUserBuffer或RtlCompareUnicodeString.因为同样的原因,post-oper callback例程中使用的一切数据结构都必须从非分页池中分配.
以下情形是上述规则的几个例外:
· 如果minifilter驱动的pre-oper callback例程为一个IRP-I/O操作返回了FLT_PREOP_SYNCHRONIZE,相应的post-oper callback例程就会在IRQL <= APC_LEVEL及在与pre-oper callback例程相同的线程上下文中被调用.
· Fast I/O操作的post-oper callback例程在IRQL = PASSIVE_LEVEL及在与pre-oper callback例程相同的线程上下文中被调用.
· Post-create callback例程在IRQL = PASSIVE_LEVEL及发起这个IRP_MJ_CREATE操作的线程上下文中被调用.
当filter管理器为一个给定的I/O操作调用某个minifilter驱动的post-oper callback例程时,该minifilter驱动临时地控制此I/O操作.这个minifilter驱动在它做了以下事情之一时才会返回控制权:
· 从post-oper callback例程中返回FLT_POSTOP_FINISHED_PROCESSING.
· 从已经处理了一个在post-oper callback例程中被pend的IRP-I/O操作的工作历程中调用FltCompletePendedPostOperation.
本节包括:
1.执行一个I/O操作的完成处理
minifilter驱动的post-oper callback例程会在底层文件系统或legacy过滤器或minifilter驱动实例栈中有lower altitude的另外一个minifilter驱动已经完成了一个I/O操作时被调用.
另外,当某个minifilter驱动实例正被拆卸时,filter管理器会"耗尽"该实例已经接收到其pre-oper callback而正等候其post-oper callback的I/O操作.在此情形之下,即使这个I/O操作还没有被完成filter管理器也会调用该minifilter驱动的post-oper callback例程并在入参Flag中设置标记 FLTFL_POST_OPERATION_DRAINING .
当设置了FLTFL_POST_OPERATION_DRAINING标记时,该minifilter驱动一定不可以执行常规的完成处理.它应该只执行必要的cleanup,比如释放它在pre-oper callback例程中的CompletionContext参数分配的内存并返回FLT_POSTOP_FINISHED_PROCESSING.
本节内容还包括以下这篇文章:
确保完成处理的执行在安全的IRQL
如写Post-oper Callback例程一文中提到的, IRP-I/O操作的post-oper callback例程 可以在DISPATCH_LEVEL调用,除非minifilter驱动的pre-oper callback例程通过返回FLT_PREOP_SYNCHRONIZE同步了这个操作或此操作是一个create操作(create操作本来就是同步的). (更多关于这个返回值的信息,参考 返回FLT_PREOP_SYNCHRONIZE.)
不过,对还没有被同步的IRP-I/O操作来说,minifilter驱动可以两种技术来确保完成处理的执行在IRQL <= APC_LEVEL.
第一种技术是post-oper callback例程pend I/O操作直到完成例程可以在IRQL <= APC_LEVEL被执行.此技术的描述参考文章在post-oper callback例程中pend一个I/O操作.
第二种技术是post-oper callback例程调用FltDoCompletionProcessingWhenSafe. 仅当当前IRQL >= DISPATCH_LEVEL时FltDoCompletionProcessingWhenSafe才会pend I/O操作.否则,这个例程会直接执行minifilter驱动的SafePostCallback 例程. 此技术的描述参考文章FltDoCompletionProcessingWhenSafe.
2.在post-oper callback例程中pend一个I/O操作
Minifilter驱动的post-oper callback例程 可以通过执行以下步骤来pend一个I/O操作:
1. 调用FltAllocateDeferredIoWorkItem来为此I/O操作分配一个工作项.
2. 调用 FltQueueDeferredIoWorkItem来把此I/O操作发给一个系统工作队列.
3. 返回FLT_POSTOP_MORE_PROCESSING_REQUIRED.
注意如果以下条件为真则对FltQueueDeferredIoWorkItem的调用将会失败:
· 该操作不是IRP-I/O操作.
· 该操作是分页I/O操作.
· 当前线程的TopLevelIrp域不为NULL. (更多如何找出此域的值的信息参考IoGetTopLevelIrp.)
· 该操作的目标实例正被拆卸. (filter管理器通过在post-oper callback例程的入参Flags 中设置FLTFL_POST_OPERATION_DRAINING标记来指示这个情况.)
Minifilter驱动必须准备好处理失败的情形.如果你的minifilter驱动不能处理这样的失败,你就应该考虑使用返回FLT_PREOP_SYNCHRONIZE 中的技术来取代pend该操作.
在minifilter驱动的post-oper callback例程返回FLT_POSTOP_MORE_PROCESSING_REQUIRED之后,filter管理器将不会对该I/O操作进行任何深入处理直到minifilter驱动的工作例程调用 FltCompletePendedPostOperation来把操作的控制权返回给filter管理器. 在此情形之下filter管理器不会做任何深入处理即使工作线程在操作的callback数据结构的IoStatus.Status域中设置了一个失败的NTSTATUS值.
出列和执行该I/O操作的完成的工作例程必须调用 FltCompletePendedPostOperation来把操作的控制权返回给filter管理器.
3.在post-oper callback例程中令一个I/O失败
Minifilter驱动的post-oper callback例程 可以令一个成功的I/O操作失败,但是简单地令一个I/O操作失败不会撤销该操作的影响.minifilter驱动要负责执行撤销该操作的一切所需的处理.
例如,minifilter驱动的post-create callback例程可以通过执行以下步骤来令一个成功的IRP_MJ_CREATE操作失败:
1. 调用FltCancelFileOpen来关闭被create操作创建或打开的文件.注意FltCancelFileOpen不会撤销对文件的一切修改.例如, FltCancelFileOpen不会删除一个最新创建的文件或恢复被删减的文件到以前的大小.
2. 设置callback数据结构的IoStatus.Status域为该操作最终的NTSTATUS值.此值必须是一个有效的错误NTSTATUS值,比如 STATUS_ACCESS_DENIED.
3. 设置callback数据结构的IoStatus.Information域为零.
4. 返回FLT_POSTOP_FINISHED_PROCESSING.
当设置callback数据结构的IoStatus.Status域为该操作最终的NTSTATUS值时,minifilter驱动必须指定一个有效的错误NTSTATUS值.注意这个minifilter驱动不可以指定STATUS_FLT_DISALLOW_FAST_IO;只有filter管理器可以使用这个NTSTATUS值.
FltCancelFileOpen的调用者必须运行在IRQL <= APC_LEVEL.不过,一个minifilter驱动可以从post-create callback例程中安全地调用这个例程,因为, 对IRP_MJ_CREATE操作来说,post-oper callback例程在PASSIVE_LEVEL及发起这个create操作的线程的上下文中被调用.
五、修改一个I/O操作的参数
Minifilter驱动可以修改一个I/O操作的参数.例如,一个minifilter驱动的pre-oper callback例程 可以通过改变操作的目标实例来把一个I/O操作重定向到另一个卷上. 新的目标实例必须是同一minifilter驱动在另一卷上并处于同一altitude的一个实例.
I/O操作的参数在这个操作的callback数据(FLT_CALLBACK_DATA)结构和I/O参数块(FLT_IO_PARAMETER_BLOCK)结构中.minifilter驱动的pre-oper callback例程和post-oper callback例程会在入参Data中获得此操作的callback数据结构指针.callback数据结构的成员Iopb是一个I/O参数块结构(它包含这个操作的参数)的指针.
如果minifilter驱动的pre-oper callback例程修改了某个I/O操作的参数,则minifilter驱动实例栈中在它之下的所有minifilter驱动都会在它们的pre-oper和post-oper callback例程中收到修改后的参数.
已修改的参数不会被当前minifilter驱动的post-oper callback例程或minifilter驱动栈中一切在它之上的minifilter驱动接收到.在所有情况下,一个minifilter驱动的pre-oper和post-oper callback例程都会接收到一个给定的I/O操作的相同的入参值.
在修改了一个I/O操作的参数之后,pre-oper或post-oper callback例程必须指示通过调用FltSetCallbackDataDirty已经这样做了(即修改了参数),除非它已经改变了callback数据结构的IoStatus域的内容.否则,过滤器管理器会忽视对参数值的一切改变. FltSetCallbackDataDirty会在I/O操作的callback数据结构中设置FLTFL_CALLBACK_DATA_DIRTY标记. Minifilter驱动可以调用FltIsCallbackDataDirty测试此标记或调用FltClearCallbackDataDirty来清除这个标记.
如果某个minifilter驱动的pre-oper callback例程修改了一个I/O操作的参数,所有minifilter驱动实例栈中在其之下的minifilter驱动都会在它们的pre-oper和post-oper callback例程的入参Data和FltObjects中收到已修改的参数. (Minifilter驱动不可以直接修改被参数FltObjects指向的FLT_RELATED_OBJECTS结构的内容.不过,如果某个minifilter驱动修改了一个I/O操作的目标实例或目标文件对象,那么filter管理器就会修改要床给lower minifilter驱动的FLT_RELATED_OBJECTS结构中相应的Instance或FileObject成员的值.)
尽管minifilter驱动的pre-oper callback例程做出的一切参数改变都不会被它自己的post-oper callback例程收到,但它的pre-oper callback例程能够把已改变的的参数信息传给它自己的post-oper callback例程.如果pre-oper callback例程通过返回FLT_PREOP_SUCCESS_WITH_CALLBACK或者FLT_PREOP_SYNCHRONIZE来沿着栈向下传递I/O操作,它可以在被出参CompletionContext指向的minifilter驱动定义的结构中存储已改变参数的信息. Filter管理器会把这个结构的指针传到post-oper callback例程的入参CompletionContext中.
更多关于I/O操作的参数的信息参考FLT_CALLBACK_DATA和 FLT_IO_PARAMETER_BLOCK.