檔案系統Minifilter驅動(八)
寫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例程呼叫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例程