Windows驅動_檔案系統微小過濾驅動之三微小過濾驅動的操作
30歲左右的程式設計師,現在除了奮鬥以外,要開始考慮下自己的身體了,到了這個年紀,不能像之前20歲左的年輕人一樣不顧一切去拼搏。現在的自己,應該更講究效率。所以選擇公司也很重要。同樣,運動開始變得必需了,要開始自己的運動計劃,其實也是為了自己有更好的本錢去奮鬥。去努力,還有一個考慮是這個年齡的程式設計師,已經不是一個人,正常的情況下,有兒子有老婆了,你的身體就變得更加的重要。所以,我們要愛惜自己的身體,為自己,為家人,為以後。
Writing a DriverEntry Routine for a Minifilter Driver
微小過濾驅動的DriverEntry例程執行一些全域性變數的初始化,驅動註冊,初始化過濾。這個例程執行在IRQL=PASSIVE_LEVEL的執行緒上下文。
Registering the Minifilter Driver
在DriverEntry例程中呼叫FltRegisterFilter例程,加自己加入到已經註冊的微小過濾驅動的列表中去,並給過濾管理器提供一組供其呼叫的回答函式。
NTSTATUS status;
status = FltRegisterFilter(
DriverObject, //Driver
&FilterRegistration, //Registration
&MiniSpyData.FilterHandle); //RetFilter
這裡,我們主要看第二個引數,是一個輸入引數,型別是FLT_REGISTRATION.
typedef struct _FLT_REGISTRATION {
USHORT Size;
USHORT Version;
FLT_REGISTRATION_FLAGS Flags;
const FLT_CONTEXT_REGISTRATION *ContextRegistration;
const FLT_OPERATION_REGISTRATION *OperationRegistration;
PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback;
PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback;
PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback;
PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback;
PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback;
PFLT_GENERATE_FILE_NAME GenerateFileNameCallback;
PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback;
PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback;
#if FLT_MGR_LONGHORN
PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback;
PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback;
#endif
#ifdef FLT_MFG_WIN8
PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback;
#endif
} FLT_REGISTRATION, *PFLT_REGISTRATION;
這個結構體包含了所有的微小過濾驅動可以處理的操作的回撥函式的地址。我們一個一個往下看:
typedef struct _FLT_CONTEXT_REGISTRATION {
FLT_CONTEXT_TYPE ContextType;
FLT_CONTEXT_REGISTRATION_FLAGS Flags;
PFLT_CONTEXT_CLEANUP_CALLBACK ContextCleanupCallback;
SIZE_T Size;
ULONG PoolTag;
PFLT_CONTEXT_ALLOCATE_CALLBACK ContextAllocateCallback;
PFLT_CONTEXT_FREE_CALLBACK ContextFreeCallback;
PVOID Reserved1;
} FLT_CONTEXT_REGISTRATION, *PFLT_CONTEXT_REGISTRATION;
這個我們在上面有看到過,這個是關於物件的上下文空間操作相關的回撥函式,但是這個結構用得比較少。
再看下一個:
typedef struct _FLT_OPERATION_REGISTRATION {
UCHAR MajorFunction;
FLT_OPERATION_REGISTRATION_FLAGS Flags;
PFLT_PRE_OPERATION_CALLBACK PreOperation;
PFLT_POST_OPERATION_CALLBACK PostOperation;
PVOID Reserved1;
} FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
我們之前講到的,IO操作的預籤延後操作的回撥函式例程在這裡定義。
這裡,主要是MajorFunction這個域,關於裝置的我找到了一些,忽然發現,關於檔案的比這多。
Major Function Code Hexadecimal Code
IRP_MJ_CREATE 0x00
IRP_MJ_CREATE_NAMED_PIPE 0x01
IRP_MJ_CLOSE 0x02
IRP_MJ_READ 0x03
IRP_MJ_WRITE 0x04
IRP_MJ_QUERY_INFORMATION 0x05
IRP_MJ_SET_INFORMATION 0x06
IRP_MJ_QUERY_EA 0x07
IRP_MJ_SET_EA 0x08
IRP_MJ_FLUSH_BUFFERS 0x09
IRP_MJ_QUERY_VOLUME_INFORMATION 0x0A
IRP_MJ_SET_VOLUME_INFORMATION 0x0B
IRP_MJ_DIRECTORY_CONTROL 0x0C
IRP_MJ_FILE_SYSTEM_CONTROL 0x0D
IRP_MJ_DEVICE_CONTROL 0x0E
IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_SCSI 0x0F
IRP_MJ_SHUTDOWN 0x10
IRP_MJ_LOCK_CONTROL 0x11
IRP_MJ_CLEANUP 0x12
IRP_MJ_CREATE_MAILSLOT 0x13
IRP_MJ_QUERY_SECURITY 0x14
IRP_MJ_SET_SECURITY 0x15
IRP_MJ_POWER 0x16
IRP_MJ_SYSTEM_CONTROL 0x17
IRP_MJ_DEVICE_CHANGE 0x18
IRP_MJ_QUERY_QUOTA 0x19
IRP_MJ_SET_QUOTA 0x1A
IRP_MJ_PNP
IRP_MJ_MAXIMUM_FUNCTION 0x1B
關於檔案的好像比這多。
IRP_MJ_CREATE
IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_DIRECTORY_CONTROL
IRP_MJ_FILE_SYSTEM_CONTROL
IRP_MJ_LOCK_CONTROL
IRP_MJ_PNP
IRP_MJ_QUERY_EA
IRP_MJ_QUERY_INFORMATION
IRP_MJ_QUERY_QUOTA
IRP_MJ_QUERY_SECURITY
IRP_MJ_QUERY_VOLUME_INFORMATION
IRP_MJ_READ
IRP_MJ_SET_EA
IRP_MJ_SET_INFORMATION
IRP_MJ_SET_QUOTA
IRP_MJ_SET_SECURITY
IRP_MJ_SET_VOLUME_INFORMATION
IRP_MJ_WRITE
IRP_MJ_ACQUIRE_FOR_MOD_WRITE
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE
IRP_MJ_MDL_READ
IRP_MJ_MDL_READ_COMPLETE
IRP_MJ_MDL_WRITE_COMPLETE
IRP_MJ_NETWORK_QUERY_OPEN
IRP_MJ_PREPARE_MDL_WRITE
FLT_PARAMETERS for IRP_MJ_RELEASE_FOR_MOD_WRITE
IRP_MJ_VOLUME_MOUNT
The following I/O operations do not have parameters:
IRP_MJ_ACQUIRE_FOR_CC_FLUSH
IRP_MJ_CLEANUP
IRP_MJ_CLOSE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_RELEASE_FOR_CC_FLUSH
IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION
IRP_MJ_SHUTDOWN
IRP_MJ_VOLUME_DISMOUNT
當然這裡也應該包括檔案快速IO.
NTSTATUS status;
status = FltRegisterFilter(
DriverObject, //Driver
&FilterRegistration, //Registration
&MiniSpyData.FilterHandle); //RetFilter
這裡的第三個引數是一個輸入引數,是一個過濾控制代碼。
我們可以根據這個控制代碼,呼叫FltStartFiltering函式,通知過濾管理器,微小過濾驅動已經準備附加到捲上
status = FltStartFiltering( MiniSpyData.FilterHandle );
if( !NT_SUCCESS( status )) {
FltUnregisterFilter( MiniSpyData.FilterHandle );
}
Returning Status from a Minifilter DriverEntry Routine
在DriverEntry中應該返回合適的NTSTATUS值,如果不是成功的NTSTATUS值被返回FilterUnloadCallback
例程不會被呼叫,在這種情況下,DriverEntry例程必須釋放所有分配的系統資源。
When the FilterUnloadCallback Routine Is Called
非強制的解除安裝
Non-mandatory unload.這種型別的解除安裝,在使用者模式的應用程式呼叫FilterUnload,或者核心模式驅動呼叫
FltUnloadFilter,或者通過命令fltmc unload發生。
Mandatory unload:這裡型別的解除安裝當通過sc stop 或net stop服務請求,使用者模式應用程式呼叫
ControlService函式,傳遞SERVICE_CONTROL_STOP控制碼的時候發生。
對於非強制的解除安裝,如果微小過濾驅動的FilterUnloadCallback例程返回一個錯誤或者警告值,比如
STATUS_FLT_DO_NOT_DETACH,過濾管理器不會解除安裝驅動。
對於強制解除安裝,不管FilterUnloadCallback返回什麼值,過濾管理器都會解除安裝驅動。
為了去關閉強制解除安裝功能,微小過濾可以設定FLT_REGISTRATION結構體的Flags成員為
FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP標誌。
注意,在系統關機的時候,FilterUnloadCallback例程不會被呼叫。微小過濾驅動應該在
IRP_MJ_SHUTDOWN的預前操作的回撥例程中執行一些操作。
Writing a FilterUnloadCallback Routine
在FilterUnloadCallback例程中,主要執行如下的事情:
關閉所有核心模式下開啟的交流服務埠的控制代碼。
呼叫 FltCloseCommunicationPort,當用戶模式已經打開了一個到交流服務埠的連線,這些連線在
FltCloseCommunicationPort仍然保持。
呼叫FltUnRegisterFilter去反註冊微小過濾驅動。
呼叫這個函式會導致如下事情發生:
微小過濾驅動的回撥例程被解除安裝。
微小驅動的例項被解除安裝, InstanceTeardownStartCallback and InstanceTeardownCompleteCallback對於
每一個過濾驅動的例項都會被呼叫。
如果在卷,例項,流,流控制代碼上設定了上下文,這些上下文被刪除,如果過濾驅動註冊了CleanupContext
回撥例程,過濾管理器在刪除這些上下文之前會呼叫這些回撥函式。如果有任何引用這個不透明的過濾控制代碼發生,
FltUnregisterFilter會進入等待狀態,直到他們被刪除。比如,過濾驅動呼叫FltQueueGenericWorkItem插入一個工作
到系統工作佇列中,當這個工作專案還沒有被處理。(過濾管理器將呼叫刪除引擎在過濾驅動呼叫
FltQueueGenericWorkItem然後在工作例程中將其刪除)。
還有一種情況是,過濾驅動呼叫呼叫了 FltObjectReference or FltGetFilterFromInstance,但是沒有呼叫
FltObjectDereference的情況下。
執行任何全域性性的清除操作。
呼叫ExDeleteResourceLite去刪除預前通過呼叫ExInitializeResourceLite分配的全域性資源。
呼叫ExFreePool或者ExFreePoolWithTag去釋放任何預前通過ExAllocatePoolWithTag分配的全域性記憶體。
呼叫 ExDeleteNPagedLookasideList or ExDeletePagedLookasideList 刪除預前通過呼叫
ExInitializeNPagedLookasideList or ExInitializePagedLookasideList分配的lookaside.
呼叫Call PsRemoveCreateThreadNotifyRoutine or PsRemoveLoadImageNotifyRoutine 去
刪除預前通過PsSetCreateThreadNotifyRoutine or PsSetLoadImageNotifyRoutine, 分配的全域性回撥例程。
返回合適的NTSTATUS值。
一般返回STATUS_SUCCESS,為了重用不是強制的解除安裝操作,微小過濾驅動應該呼叫合適的警告或者錯誤值比如STATUS_FLT_DO_NOT_DETACH.
Writing Preoperation and Postoperation Callback Routines
微小過濾驅動可以為其想過濾的每一種IO操作註冊一個預籤操作回撥函式和一個延後操作回撥函式。
和原始的檔案系統過濾驅動不一樣,微小過濾驅動可以有選擇的處理IO操作。它可以只為感興趣的IO操作註冊預前或者延後操作回撥例程。
預前操作回撥例程和原始過濾驅動模式的派遣例程相類似。當過濾管理器處理IO操作的時候,它首先檢視在微小過濾驅動例項堆疊中為這個IO操作已經註冊了回撥函式的微小過濾驅動,然後按照約定的順序依次呼叫這些回撥函式。最上層的函式,其例項有最高的高度值,除非微小過濾驅動已經完成了IO操作,否則,過濾管理器不會將操作發往原始過濾驅動和檔案系統。
延後操作的回撥例程和原始過濾驅動模式的完成例程很類似。完成例程在IO管理器將其操作傳遞到下層,等到處理,操作完成的時候,上層的完成例程被得到處理。過濾管理器首先呼叫有最低高度值的例項的延後操作的回撥函式。依次向上傳遞呼叫。
Registering Preoperation and Postoperation Callback Routines
面我們講到,我們註冊預前和延後操作回撥函式是通過一個結構體,在FltReigisterFilter中註冊的,這個結構體是 FLT_OPERATION_REGISTRATION 。
陣列中每一個 FLT_OPERATION_REGISTRATION 結構體都是一樣的,除了最後一個。每一個都包含如下資訊。
操作的主功能碼。
對於讀寫操作(IRP_MJ_READ and IRP_MJ_WRITE),需要設定flags,指定是否是快取IO還是分頁IO.
一個預前操作回撥函式的入口點和一個延後操作回撥函式的入口點。
陣列中最後一個元素必須是{IRP_MJ_OPERATION_END}
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}
};
Filtering I/O Operations in a Minifilter Driver
如下是一些註冊的約定。
IRP_MJ_CREATE的預前回調函式不能為檔案,流,流的控制代碼,查詢或者設定上下文。因為在預前建立的時間,將建立的檔案或者流還沒有被發現。
IRP_MJ_CLOSE的延後操作的回撥函式不能為檔案,流,和流的控制代碼設定或查詢上下文。因為那些和其關聯的系統內部結構在延後回撥函式呼叫前被釋放了。
對於IRP_MJ_CLEANUP或IRP_MJ_CLOSE操作,微小過濾驅動不應該返回失敗。這些操作可以被暫停,返回給過濾管理器,通過STATUS_SUCCESS,然而,預前操作回撥例程不應該返回失敗。
微小過濾驅動不能夠為IRP_MJ_SHUTDOWN註冊延後操作的回撥函式。
Writing Preoperation Callback Routines
如果例程對操作不進行干預,也不需要操作的最後的狀態,或者沒有為這個操作註冊延後回撥函式。 傳遞IO操作沒有延後操作回撥函式 返回FLT_PREOP_SUCCESS_NO_CALLBACK需要操作的最後狀態。傳遞操作,需要呼叫延後操作回撥函式。 返回FLT_PREOP_SUCCESS_WITH_CALLBACK
過濾驅動必須結束或者向在將來繼續進行處理
將操作放在暫停狀態,然後使用FltComplete 返回FLT_PREOP_PENDING.
PendedPreOperation.
延後操作處理必須和派遣例程的呼叫在同一個執行緒上下文空間裡,這樣確保IRQL和本地變數一致。 延後操作的同步
返回FLT_PREOP_SYNCHRONIZE
預前操作回撥例程需要結束操作
停止操作,分配一個最終的NTSTATUS值 返回FLT_PREOP_COMPLETE
像派遣例程一樣,預前操作的回撥例程在IRQL=PASSIVE_LEVEL或IRQL=APC_LEVEL上被呼叫。通常在IRQL=PASSIVE_LEVEL
的IO請求的原始執行緒上下文上被呼叫。對於快速IO和檔案系統過濾操作,預前回調例程一直在IRQL=PASSIVE_LEVEL上被呼叫。
然而,對於基於IRP的操作,如果更高一層過濾或者微小過濾驅動通過工作執行緒暫停了操作流程,微小過濾驅動的預前回調例程可以在系統工作執行緒的上下文被呼叫。
上下文物件不能在IRQL > APC_LEVEL的延後操作回撥例程中得到,而是,通過在預前操作得到上下文物件傳遞給延後操作的回撥例程,或在IRQL <= APC_LEVEL上執行處理。
當過濾管理器對於給定的IO操作,呼叫微小過濾驅動的預前回調例程時,微小過濾驅動臨時控制IO操作。微小過濾驅動將重新得到控制直到如下事情發生:
在預前操作回撥函式中返回一個不是FLT_PREOP_PENDING的返回值。
在工作例程中呼叫FltCompletePendedPreOperation結束已經在預前回調函式中已經被暫停的操作。
Passing an I/O Operation Down the Minifilter Driver Instance Stack
當微小過濾驅動的預前回調函式或工作例程返回一個IO操作給過濾管理器,過濾管理器傳送操作給微小過濾驅動例項堆疊的下層微小過濾驅動和原始過濾驅動和檔案系統進行處理。
微小過濾的預前回調例程通過如下IO操作的狀態值返回給過濾管理器。
FLT_PREOP_SUCCESS_NO_CALLBACK (all operation types)
FLT_PREOP_SUCCESS_WITH_CALLBACK (all operation types)
FLT_PREOP_SYNCHRONIZE (IRP-based I/O operations only)
雖然,FLT_PREO_SYNCHRONIZE應該僅僅只對預基於IRP的IO操作的返回,你也可以對另外的一些操作返回這些值。如果對不是基於IRP的IO操作返回這個值,過濾管理器將這個值當FLT_PREOP_SUCCESS_WITH_CALLBACK返回。
對於在預前回調例程中暫停的需要在工作例程進行處理的操作,返回IO操作的狀態值是通過呼叫FltCompletePendedPreOperation重啟暫停的IO插座後,設定CallbackStatus引數值返回的。
Returning FLT_PREOP_SUCCESS_WITH_CALLBACK
如果在微小過濾驅動的預前回調例程中返回FLT_PREOP_SUCCESS_WITH_CALLBACK,過濾管理器呼叫在IO完成的處理中呼叫微小過濾驅動的延後操作的回撥函式。
注意:如果微小過濾驅動的預前回調函式返回FLT_PREOP_SUCCESS_WITH_CALLBACK,但是微小過濾驅動沒有為其操作註冊延後處理的回撥函式,系統會發出了一個斷言。
如果微小過濾驅動的預前回調函式返回FLT_PREOP_SUCCESS_WITH_CALLBACK,它可以在其輸出引數中CompletionContext返回一個非空值。這個引數是一個可選的上下文指標,它會作為引數傳遞給延後操作的回撥例程。延後操作的回撥例程在其CompleteContext輸入引數中接收到這個指標。
FLT_PREOP_SUCCESS_WITH_CALLBACK狀態值可以被用來返回所有型別的IO操作。
Returning FLT_PREOP_SUCCESS_NO_CALLBACK
如果微小過濾驅動的預前操作的回撥例程返回FLT_PREOP_SUCCESS_NO_CALLBACK,過濾管理器在IO完成處理中不會呼叫微小過濾驅動的延後操作的處理例程。
如果微小過濾驅動的預前操作的回撥函式返回FLT_PREOP_SUCCESS_NO_CALLBACK,它必須在其輸出引數CompletionContext中返回NULL.
FLT_PREOP_SUCCESS_NO_CALLBACK狀態值可以被用來返回所有型別的IO操作。
Returning FLT_PREOP_SYNCHRONIZE
如果微小過濾驅動的預前回調函式例程中通過返回FLT_PREOP_SYNCHRONIZE來同步IO操作,過濾管理器在IO完成處理中,呼叫微小過濾驅動的延後操作的回撥函式。
過濾管理器呼叫微小過濾驅動的延後操作的回撥函式在和預前回調函式在同一個執行緒上下文空間上,在IRQL <= APC_LEVEL(這個是執行緒上下文,不需要是原始執行緒上下文)。
注意:如果微小過濾驅動的預前回調函式例程返回了FLT_PREOP_SYNCHRONIZE,但是,微小過濾驅動沒有為其IO操作,註冊延後回撥例程,系統將發出一個斷言。
如果微小過濾驅動的預前操作的回撥例程返回FLT_PREOP_SYNCHRONIZE,它可以在其輸出引數CompletionContext中返回一個非空值。這個引數是一個上下文空間的指標做為引數會傳遞給相應的延後操作的回撥函式。延後操作的回撥函式在CompletionConext輸入引數中接收這個指標值。
微小過濾驅動的預前回調函式例程應該只對於基於IRP的IO操作返回FLT_PREOP_SYNCHRONIZE,如果,對於另外的操作型別,返回了這個值,過濾管理器將這個值當成FLT_PREOP_SUCCESS_WITH_CALLBACK看待。
為了去探測是不是基於IRP的IO茶軸,可以使用FLT_IS_OPERATION巨集。
微小過濾驅動在其預前操作的回撥函式中,對於建立操作,不應該返回FLT_PREOP_SYNCHRONIZE,因為這些操作已經被過濾管理器同步了。如果微小過濾驅動為IRP_MJ_CREATE操作已經註冊了預前操作和延後操作的回撥函式,延後操作的回撥函式在IRQL=PASSIVE_LEVEL上被呼叫,和預前操作的回撥函式在同一個執行緒上下文空間中。
微小過濾驅動禁止在其非同步讀或寫操作返回FLT_PREOP_SYNCHRONIZE,這樣操作,會嚴重降低微小過濾驅動和系統的表現,甚至會引起死鎖。修改頁寫的執行緒被阻塞。在對基於IRP的讀寫操作,返回FLT_PREOP_SYNCHRONIZE之前,微小過濾驅動應該呼叫FltIsOpeationSynchronous函式檢查操作是不是同步的。
如下的IO操作不能被同步:
對於檔案系統控制操作的鎖操作(FSCTL)(MajorFunction是IRP_MJ_FILE_SYSTEM_CONTROL,FsControlCode是FSCTL_REQUEST_FILTER_OPLOCK, FSCTL_REQUEST_BATCH_OPLOCK, FSCTL_REQUEST_OPLOCK_LEVEL_1, or FSCTL_REQUEST_OPLOCK_LEVEL_2)
改變目錄操作的通知(MajorFunction is IRP_MJ_DIRECTORY_CONTROL; MinorFunction is IRP_MN_NOTIFY_CHANGE_DIRECTORY).
基於位元組的鎖請求(MajorFunction is IRP_MJ_LOCK_CONTROL; MinorFunction is IRP_MN_LOCK).
對於這些操作,不能使用FLT_PREOP_SYNCHRONIZE 返回。
Completing an I/O Operation in a Preoperation Callback Routine
結束IO操作意味著停止處理操作,發配一個NTSTAUS值,返回給過濾管理器。
當微小過濾驅動結束一個IO操作,過濾管理器執行如下的動作:
將操作不發往微小過濾驅動的下層微小過濾驅動,原始過濾驅動,或檔案系統。
呼叫微小過濾驅動例項堆疊中的上層微小過濾過濾驅動的延後處理回撥例程。
如果當前的微小過濾驅動存在延後處理的回撥例程,過濾管理器不會呼叫它。
微小過濾驅動的預前操作的回撥例程通過如下的步驟結束IO操作。
1,為操作設定回撥資料結構的IoStatus.Status域為合適NTSTATUS值。
2,返回FLT_PREOP_COMPLETE.
預前操作的回撥例程結束IO操作的時候,不要設定輸出引數CompletionContext為一個非空值。
微小過濾驅動也可以通過如下的步驟,在預前暫停的IO操作,在工作例程將其結束。
1,為操作設定回撥資料結構的IoStatus.Status域為合適NTSTATUS值。
2,在呼叫FltCompletePendedPreOperation的時候在其CallbackStatus傳遞為FLT_PREOP_COMPLETE.
當結束一個IO操作的時候,微小過濾驅動必須設定回撥資料結構的IoStatus.Status域為一個最終的NTSTATUS值,但是這個NTSTATUS值不能是一個STATUS_PENDING或STATUS_FLT_DISALLOW_FAST_IO值。對於清除或關閉操作,這個預必須是STATUS_SUCCESS.這些操作不能是任何其他的NTSTATUS值。
結束IO操作,通常涉及到操作的成功還是失敗,取決預NTSTATUS值。
為了成功IO操作,必須用一個代表成功或NTSTATUS資訊值,比如STATUS_SUCCESS.
為了失敗IO操作,可以使用一個代表失敗或警告的NTSTATUS值,比如STATUS_INVALID_DEVICE_REQUEST or STATUS_BUFFER_OVERFLOW.
NTSTATUS值定義在ntstatus.h中,這些值分為四類,成功,資訊,警告和錯誤。
Disallowing a Fast I/O Operation in a Preoperation Callback Routine
在某些情況下,微小過濾驅動可以選擇禁止快速IO操作,而不是完成它。禁止快速IO操作可以避免快速IO的路徑被用於操作。
和結束IO操作一樣,禁止快速IO也意味著停止處理並將操作返回給過濾管理器。但是,禁止快速IO操作又跟完成它不一樣。如果微小過濾驅動禁止了一個從IO管理器傳送過來的一個快速IO,IO管理器可能重新發出一個基於IRP等價的操作。
當微小過濾驅動在預前操作的回撥例程中禁止快速IO操作的時候,過濾管理器做如下的事情:
不會將請求發往微小過濾驅動的下層微小過濾驅動,原始過濾驅動,或檔案系統。
呼叫當前微小過濾驅動例項堆疊的上層所有的微小過濾驅動延後操作的回撥例程。
如果當前微小過濾驅動的延後操作回撥例程存在,不呼叫它。
微小過濾驅動禁止一個快速IO操作,是通過在其預前操作回撥例程中返回FLT_PREOP_DISALLOW_FASTIO實現的。
預前操作的回撥例程不應該設定回撥資料結構的IoStatus.Status域,因為過濾管理器會自動將其設定為STATUS_FLT_DISALLOW_FAST_IO.FLT_PREOP_DISALLOW_FASTIO僅僅只能對快速IO操作返回。為了去探測操作是不是快速IO操作,看FLT_IS_FASTIO_OPERATION.
微小過濾驅動不能對IRP_MJ_SHUTDOWN,IRP_MJ_VOLUME_MOUNT,或IRP_MJ_VOLUME_DISMOUNT操作返回FLT_PREOP_DISALLOW_FASTIO.
Pending an I/O Operation in a Preoperation Callback Routine
微小過濾驅動的預前回調例程可以返回FLT_PREOP_PENDING暫停IO操作,將操作發往系統工作佇列中處理。返回這個狀態值,宣告微小過濾驅動重新獲得IO操作的控制,直到呼叫FltCompletePendedPreOperation恢復IO操作的處理後。
微小過濾驅動的預前操作回撥例程通過如下的步驟來暫停IO操作:
1,通過呼叫例程FltQueueDeferredIoWorkItem,傳送IO操作到系統工作佇列。
2,返回FLT_PREOP_PENDING.
微小過濾驅動必須暫停所有接收到的IO操作,不應該呼叫FltQueueDeferredIoWorkItem去暫停操作,因為呼叫這個例程將導致系統工作佇列被衝突。而是,微小過濾驅動應該使用安全取消佇列。可以看FltCbdqInitialize.
在下面的條件發生時,呼叫FltQueueDeferredIoWorkItem會失敗:
操作不是基於IRP的IO操作。
操作是一個分頁的IO操作。
當前執行緒的TopLevelIrp域不為空(怎樣去找到這個值可以看IoGetTopLevelIrp).
如果微小過濾驅動的預籤操作的回撥函式返回FLT_PREOP_PENDING,它應該在其輸出引數CompleteContext返回NULL.
微小過濾驅動只能對基於IRP的操作返回FLT_PREOP_PENDING.為了去探測操作是否是基於IRP的,看FLT_IS_IRP_OPERATION巨集。
從佇列中移除並處理IO操作的工作例程,應該呼叫FltCompletePendedPreOperation去恢復操作的處理。
Writing Postoperation Callback Routines
在延後操作的回撥例程中可以做如下的操作;
在回撥例程中直接完成工作。所有的完成工作都必須在IRQL <= DISPATCH_LEVEL上進行。
完成結束工作在安全的IRQL上,返回FLT_STATUS_MORE_PROCESSING_REQUIRED將工作重新排到工作執行緒中去。當工作執行緒呼叫FltCompletePendedPostOperation進行延後的處理。
安全取消建立操作。
延後操作的回撥例程和原始過濾驅動中的完成例程很類似。
延後操作的回撥例程的註冊和預前操作的回撥例程的註冊是一樣的。
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
);
像完成例程一樣,延後操作的回撥例程也是在IRQL <= DISPATCH_LEVEL的執行緒上下文空間被呼叫。
因為可以在IRQL = DISPATCH_LEVEL上被呼叫,延後操作回撥例程不能夠呼叫那些必須在低優先順序下的例程,比如FltLockUseBuffer或RtlCompareUnicodeString.同樣的原因,在延後操作回撥例程中使用的任何資料結構都必須在非分頁記憶體空間中分配空間。
但是,如下有些情況是例外:
如果微小過濾驅動的預前操作回撥例程對於基於IRP的操作返回FLT_PREOP_SYNCHRONIZE,相應的延後操作的回撥例程在IRQL <= APC_LEVEL,和預前操作的回撥函式執行
在同一個執行緒上下文空間上。
延後操作回撥函式對於快速IO操作在IRQL = PASSIVE_LEVEL上被呼叫,和預前操作的回撥函式在同一個執行緒上下文空間上。
延後操作的回撥函式在IRQL = PASSIVE_LEVEL上被呼叫,和IRP_MJ_CREATE操作的原始執行緒上下文在同一個空間。
當過濾管理器呼叫微小過濾驅動的延後操作的回撥函式對給定的IO操作進行處理的時候,微小過濾驅動臨時控制IO操作,微小過濾驅動保留控制直到如下的事情發生:
在延後操作的回撥例程中返回FLT_POSTOP_FINISHED_PROCESSING.
在延後操作回撥例程中被暫停的基於IRP的IO操作,在工作例程中呼叫FltCompletePendedPostOperation後。
Performing Completion Processing for an I/O Operation
當IO操作被檔案系統,原始過濾驅動,或者在微小過濾驅動例項堆疊的更低高度值的微小過濾驅動完成的時候,微小過濾驅動的延後操作的回撥例程被呼叫。
另外,當微小過濾驅動的例項被解除安裝,過濾管理器對於例項在預前操作回撥例程中接收到的在延後操作回撥例程中等待的IO操作,進行釋放。在這種情況下,
過濾管理器呼叫微小過濾驅動的延後回撥函式,甚至IO操作都沒有被結束,並在其Flags的輸入引數中設定FLTFL_POST_OPERATION_DRAING.
當flag被設定為FLTFL_POST_OPERATION_DRAINING,微小過濾驅動禁止執行普通的結束操作。而是,執行任何需要的清除,比如釋放那些在其預前操作回撥分配的裝入到CompletionContext引數的記憶體。然後返回FLT_POSTOP_FINISHED_PROCESSING.
Ensuring that Completion Processing is Performed at Safe IRQL
但是對於那些基於IRP的IO操作,沒有被準備同步,可以使用如下兩種方法來確保其完成操作的執行在IRQL <= APC_LEVEL上。
第一種方法是對於在延後操作的回撥函式中暫停操作,直到完成操作被在IRQL <= APC_LEVEL上處理。
第二種方法是對於延後操作的回撥函式呼叫FltDoCompletionProcessingWhenSafe。FltDoCompletionProcessingWhenSafe僅僅在IRQL >= DISPATCH_LEVEL上暫停IO的處理。
否則,這個例程立即執行微小過濾驅動的safePostCallback例程。
Pending an I/O Operation in a Postoperation Callback Routine
微小過濾驅動的延後操作的回撥例程可以通過如下的步驟來暫停IO的操作。
1,呼叫FltAllocateDeferredIoWorkItem為IO操作分配工作專案。
2,呼叫FltQueueDeferredIoWorkItem傳送IO操作到系統的工作佇列。
3,返回FLT_POSTOP_MORE_PROCESSING_REQUIRED.
如果如下的條件為真,呼叫FltQueueDeferredIoWorkItem會失敗。
操作不是基於IRP的IO操作。
操作是一個分頁的IO操作。
當前執行緒的TopLevelIrp是不為NULL的。(可以通過IoGetTopLevelIrp得到這個域)。
IO操作的目標例項正在被解除安裝(過濾管理器宣告這個條件是通過在延後操作的回撥函式中設定輸入引數Flags為FLTFL_POST_OPERATION_DRAINING).
小過濾驅動應該準備去處理這樣的失敗。如果你的驅動不能處理這樣的失敗,你應該考慮在你的延後操作的回撥函式中返回FLT_PREOP_SYNCHRONIZE而不是暫停IO操作。
微小過濾驅動的延後操作的回撥例程返回FLT_POSTOP_MORE_PROCESSING_REQUIRED以後,過濾管理器不會在為IO操作執行任何進一步的完成操作,直到微小過濾驅動的工作例程呼叫FltCompletePendedPostOperation將控制返回給過濾管理器。過濾管理器在這種條件下,不會執行任何進一不的操作,甚至工作例程為操作設定回撥資料結構IoStatus.Status域為一個失敗的NTSTATUS值。
在工作例程中,將工作專案從佇列中取出,執行完成操作,然後呼叫FltCompletePendedPostOperation返回控制給過濾管理器。
Failing an I/O Operation in a Postoperation Callback Routine
微小過濾驅動的延後操作的回撥例程可以失敗一個IO操作,簡單的失敗一個IO操作,不會引起操作的取消。微小過濾驅動需要做一些處理讓IO操作取消。
舉例來說,微小過濾驅動的延後回撥函式可以通過如下的步驟去失敗一個IRP_MJ_CREATE操作:
1,呼叫FltCancelFiltOpen去關閉被建立的檔案或通過建立操作去開啟的檔案。注意:FltCancelFileOpen不會取消任何對檔案的修改。舉例來說:
FltCancelFileOpen不會刪除一個新建立的檔案或者或者截斷檔案裝入到它之前的大小。
2,設定回撥資料結構的IoStauts.Status域為操作的最終的NTSTATUS值。這個值必須是一個有效的錯誤的NTSTATUS值,比如STATUS_ACCESS_DENIED.
3,設定回撥資料結構的IoStatus.Information域的值為0.
4,返回FLT_POSTOP_FINISHED_PROCESSING.
FltCancelFileOpen的呼叫者必須執行在IRQL <= APC_LEVEL上,微小過濾驅動在為IRP_MJ_CREATE的延後回撥函式在可以安全的呼叫這個函式,因為其IRQL=PASSIVE_LEVEL上,
在建立操作的原始執行緒上下文空間上。
Modifying the Parameters for an I/O Operation
微小過濾驅動可以為IO操作修改引數。舉例來說,微小過濾驅動的預前操作的回撥例程可以通過改變操作的目標例項重定位IO操作到不同的卷。新的目標例項和現在的例項必須來自同一個微小過濾驅動,而且在另一個捲上有同樣的高度值。
IO操作的引數可以在回撥資料結構(FLT_CALLBACK_DATA)和為操作的IO引數快結構(FLT_IO_PARAMETER_BLOCK)中找到.微小過濾驅動的預前和延後回撥例程在其輸入引數Data中將接收到一個指向回撥資料結構的指標。回撥資料結構的lopb成員指向IO引數塊資料結構。
如果微小過濾驅動的預前回調例程為IO操作修改了其引數。那麼在微小過濾驅動例項堆疊中的下層所有的過濾驅動的預前和延後回撥例程都將接收到修改的引數。
改的引數不能被當前微小過濾驅動的延後回撥函式得到,也不能被微小過濾驅動例項堆疊中的上層所有的微小過濾驅動得到。在所有的條件下,微小過濾驅動的預前和延後回撥例程對於給定的IO操作都將接收到同樣的輸入引數值。
修改了IO操作的引數以後,預前和延後回撥例程必須呼叫FltSetCallbackDataDirty宣告它做的修改,除非,它已經修改了回撥資料結構的IoStatus域。否則,、過濾管理器將忽略對引數說做的所有修改。FltSetCallbackDataDirty為IO操作設定了回撥資料結構的flag為FLTFL_CALLBACK_DATA_DIRTY。微小過濾驅動可以通過測試flag,通過呼叫FltIsCallbackDataDirty或FltClearCallbackDataDirty.
如果微小過濾驅動預前回調例程為IO操作修改了引數,所有在微小過濾驅動例項堆疊下層的驅動在其預前和延後回撥例程的輸入引數Data和FltObjects接收到修改的引數。(微小過濾驅動不能夠直接修改FltObjects引數的內容,因為它是一個指向FLT_RELATED_OBJECT結構的指標。但是,如果微小過濾驅動為IO操作修改了目標例項或目標檔案物件,過濾管理器會修改FLT_RELATED_OBJECTS結構相應的Instance或FileObject,並將其傳遞到下層的微小過濾驅動)
雖然,在微小過濾驅動的預前回調例程中對引數的修改,其延後回撥例程中得不到通知,預前回調例程可以將其修改引數的資訊傳遞給自己的延後回撥例程。如果預前回調例程通過返回FLT_PREOP_SUCCESS_WITH_CALLBACK或FLT_PREOP_SYNCHRONIZE傳遞IO到下層堆疊,它可以將引數修改的資訊放入微小過濾驅動定義的CompletionContext輸出引數中,過濾管理器將傳遞這個結構體的指標做出輸入引數傳遞到延後操作的回撥例程中。
Determining the Buffering Method for an I/O Operation
像裝置驅動一樣,檔案系統也需要在使用者模式應用程式和系統裝置之間傳輸資料。作業系統提供如下的三種方式來訪問資料空間。
緩衝區IO方式,IO管理器為操作在非分頁記憶體區域分配一個系統空間。IO管理器從系統空間拷貝資料到應用程式的使用者空間,初始化IO操作的執行緒上下文空間中。反之亦然。
直接IO,IO管理器探測並鎖住使用者空間。然後建立一個MDL來對映鎖住的空間。IO管理器訪問初始化IO操作的執行緒上下文空間。
既不是緩衝區IO,又不是直接IO。IO管理器既不分配系統空間也不鎖住使用者空間,而是,它直接將原始的使用者模式的虛擬地址直接傳遞到檔案系統堆疊。由驅動來保證空間得之的有效性,
並在初始化的執行緒上下文執行資料拷貝操作。
微小過濾驅動必須在使用使用者模式地址前對其地址進行有效性檢驗。IO管理器和過濾管理器不會驗證這些地址,也不會驗證傳遞給微小過濾驅動的指向內部空間的指標的有效性。
所有微軟標準的檔案系統使用的一般都是第三種方式,既不是緩衝區IO,也不是直接IO.
對於基於IRP的IO操作,訪問方法的選擇必須依賴特定的操作,一般通過如下的方式:
正在執行的IO操作的型別
檔案系統卷的DEVICE_OBJECT的Flags的值。
對於IO控制操作(IOCTL)和檔案系統控制操作(FSCTL),IOCTL或FSCTL被定義的傳遞給CTL_CODE巨集TransferTye引數的值。
對於擁有空間的IO操作,通常也使用既不是緩衝區,也不是直接IO的方式。
檔案系統回撥操作沒有緩衝區。
Operations That Can Be IRP-Based or Fast I/O
下面的操作型別既可使是基於IRP也可以是快速IO型別:
IRP_MJ_DEVICE_CONTROL (IRP_MJ_INTERNAL_DEVICE_CONTROL一直是基於IRP的)。
IRP_MJ_QUERY_INFORMAITION,這個域如果FileInformationClass引數是FileBasicInformation, FileStandardInformation, or FileNetworkOpenInformation. 它是快速IO的。
IRP_MJ_READ.微小過濾驅動可以設定FLT_OPERATION_REGISTRATION結構體中的flag為 FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO,可以避免接收到快速IO的型別的IRP_MJ_READ和基於快取的IRP讀請求。
IRP_MJ_WRITE.微小過濾驅動可以設定FLT_OPERATION_REGISTRATION結構體中的flag為 FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO,可以避免接收到快速IO的型別的IRP_MJ_WRITE和基於快取的IRP寫請求。
當任何這些操作是快速IO操作,它通常使用既不是緩衝IO也不是直接IO,即使等價基於IRP的操作使用不同的訪問空間的方法。
當IRP_MJ_DEVICE_CONTROL是一個快速IO操作,它就使用既不是緩衝去也不是直接IO的訪問方法,不管IOCTL的傳輸型別。
雖然IRP_MJ_LOCK_CONTROL可以是基於IRP或者快速IO的,但是它沒有空間。
IRP-Based I/O Operations That Obey Device Object Flags
如下基於IRP的IO操作,資料訪問方法,通過檢測檔案系統卷的裝置物件的Flags的值。
IRP_MJ_DIRECTORY_CONTROL
IRP_MJ_QUERY_EA
IRP_MJ_QUERY_QUOTA
IRP_MJ_READ
IRP_MJ_SET_EA
IRP_MJ_SET_QUOTA
IRP_MJ_WRITE
DO_BUFFERED_IO和DO_DIRECT_IO在Flags成員的使用:
如果DO_BUFFERED_IO標誌被設定,操作使用緩衝區IO.
如果DO_DIRECT_IO標誌被設定,DO_BUFFERED_IO沒有被設定,操作使用直接IO.
如果許可權都沒有被設定,操作使用既不是緩衝區的也不是直接IO的。
注意:IRP_MJ_READ和IRP_MJ_WRITE可以是基於IRP的操作也可以是基於快速IO的操作。當他們是基於IRP的操作,空間訪問方法通過上面的裝置物件的flags來指定。當這些操作是基於快速IO的,它們既不使用緩衝區也不是用直接IO。
IRP-Based I/O Operations That Always Use Buffered I/O
如下基於IRP的IO操作一直使用緩衝區IO.不管檔案系統卷裝置物件的Flags的值:
IRP_MJ_CREATE (EaBuffer parameter)
IRP_MJ_QUERY_INFORMATION
IRP_MJ_QUERY_VOLUME_INFORMATION
IRP_MJ_SET_INFORMATION
IRP_MJ_SET_VOLUME_INFORMATION
注意:IRP_MJ_QUERY_INFORMATION也可以是快速IO操作。當其是快速IO的操作,既不使用緩衝區也不是用直接IO.
IRP-Based I/O Operations That Always Use Neither Buffered Nor Direct I/O
如下基於IRP的IO操作中是使用既不是緩衝區也不是直接IO的方式,不管檔案系統卷的裝置物件的Flags的值
IRP_MJ_PNP
IRP_MJ_QUERY_SECURITY
IRP_MJ_SET_SECURITY
IRP_MJ_SYSTEM_CONTROL
IRP-Based IOCTL and FSCTL Operations
如下基於IRP的IO操作,資料的訪問方法需要根據IOCTL或FSCTL中指定的傳輸型別決定。
IRP_MJ_DEVICE_CONTROL
IRP_MJ_FILE_SYSTEM_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL
傳輸型別在CTL_CODE巨集的TransferTyep引數指定,為了去得到IOCTL或FSCTL的傳輸型別,可以使用如下巨集
#define METHOD_FROM_CTL_CODE(ctrlCode) ((ULONG)(ctrlCode & 3))
巨集的返回值:
#define METHOD_BUFFERED 0
#define METHOD_IN_DIRECT 1
#define METHOD_OUT_DIRECT 2
#define METHOD_NEITHER 3
注意:IRP_MJ_DEVICE_CONTROL也可以是快速IO操作。當它是快速IO操作,它使用既不是緩衝區也不是直接IO的方式訪問,不管傳輸型別。
IRP-Based I/O Operations That Have No Buffers
如下基於IRP的IO操作沒有空間,所以也就沒有緩衝區方式訪問。
IRP_MJ_CREATE_MAILSLOT
IRP_MJ_CREATE_NAMED_PIPE
IRP_MJ_LOCK_CONTROL
Accessing the User Buffers for an I/O Operation
IO操作的結構FLT_PARAMETERS結構體包含一些為操作指定的引數,包含空間記憶體地址,空間的MDL等。
對於基於IRP的IO操作,操作的空間可以通過如下如下指定:
僅僅MDL (通常是分頁IO)
僅僅空間地址。
空間地址和MDL.
對於快速IO操作,僅僅只有使用者空間地址被指定。快速IO操作使用空間都是通過既不是緩衝區也不是直接IO,所以也沒有MDL引數。
Accessing User Buffers in a Preoperation Callback Routine
微小過濾驅動的預前回調例程對基於IRP的IO操作,應該按如下的方式來對待空間。
檢查空間是否存在相應的MDL.MDL的指標可以在操作的FLT_PARAMETERS結構體中的MdlAddress或OutputMdlAddress引數中找到。微小過濾驅動可以呼叫FltDecodeParameters去查詢MDL的指標。
一種得到有效MDL的方法是,通過尋找在回撥資料的IO引數控制塊資料結構的FLT_IO_PARAMETER_BLOCK中的成員MinorFunction的IRP_MN_MDL標誌。
NTSTATUS status;
PMDL *ReadMdl = NULL;
PVOID ReadAddress = NULL;
if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL))
{
ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress;
}
然而,IRP_MN_MDL標誌只能被設定用於讀和寫操作。最好使用FltDecodeParameters例程得到MDL,因為它對於所有的操作檢查MDL的有效性。
NTSTATUS status;
PMDL *ReadMdl = NULL;
PVOID ReadAddress = NULL;
status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
如果空間存在一個MDL,呼叫MmGetSystemAddressForMdlSafe得到空間的系統地址,然後使用這個地址訪問資料。
if (*ReadMdl != NULL)
{
ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority);
if (ReadAddress == NULL)
{
CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
CallbackData->IoStatus.Information = 0;
}
}
如果空間沒有MDL,使用空間地址去訪問資料。為了確保使用者空間的地址有效,微小過濾驅動必須使用如下例程ProbeForRead或ProbeForWrite,所有的空間引用都需要在try/except塊中。
預前操作的回撥函式在快速IO操作,應該按如下的方式對待空間。
使用空間地址去訪問資料(因為快速IO操作沒有MDL).
為了確保使用者空間地址有效,微小過濾驅動必須使用如下例程ProbeForRead或ProbeForWrite,所有的空間引用都需要在try/except塊中。
對於既可能是基於IRP又可能是快速IO型別的操作,所有的空間引用都應該在try/execpt塊中,雖然對於那些基於IRP的緩衝區IO,不需要使用這樣的安全保護。
Accessing User Buffers in a Postoperation Callback Routine
微小過濾驅動的延後回撥例程對基於IRP的IO操作,應該按如下的方式來對待空間。
檢查空間是否存在相應的MDL.MDL的指標可以在操作的FLT_PARAMETERS結構體中的MdlAddress或OutputMdlAddress引數中找到。微小過濾驅動可以呼叫FltDecodeParameters去查詢MDL的指標。
一種得到有效MDL的方法是,通過尋找在回撥資料的IO引數控制塊資料結構的FLT_IO_PARAMETER_BLOCK中的成員MinorFunction的IRP_MN_MDL標誌。
NTSTATUS status;
PMDL *ReadMdl = NULL;
PVOID ReadAddress = NULL;
if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL))
{
ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress;
}
然而,IRP_MN_MDL標誌只能被設定用於讀和寫操作。最好使用FltDecodeParameters例程得到MDL,因為它對於所有的操作檢查MDL的有效性。
NTSTATUS status;
PMDL *ReadMdl = NULL;
PVOID ReadAddress = NULL;
status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
如果空間存在一個MDL,呼叫MmGetSystemAddressForMdlSafe得到空間的系統地址,然後使用這個地址訪問資料。
if (*ReadMdl != NULL)
{
ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority);
if (ReadAddress == NULL)
{
CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
CallbackData->IoStatus.Information = 0;
}
}
如果空間沒有MDL,通過FLT_IS_SYSTEM_BUFFER巨集檢查,是否系統緩衝標誌被設定。
如果FLT_IS_SYSTEM_BUFFER巨集返回TRUE,操作使用的是緩衝區IO,空間可以在IRQL=DISPATCH_LEVEL上安全被訪問。
如果FLT_IS_SYSTEM_BUFFER巨集返回FALSE,空間不能夠在IRQL=DISPATCH_LEVEL被安全訪問。如果延後回撥例程可以在DISPATCH_LEVEL上被呼叫。它必須呼叫FltDoCompletionProcessingWhenSafe去暫停操作直到IRQL <= APC_LEVEL上被處理。FltDoCompletionProcessingWhenSafe的引數SafePostCallback指向的回撥例程,應該首先呼叫FltLockUserBuffer去鎖定空間,然後呼叫MmGetSystemAddressForMdlSafe得到空間的系統地址。
延後操作的回撥函式應該在快速IO操作,按照如下的方式來對待空間:
使用空間地址去訪問資料(對於快速IO操作,沒有MDL).
為了確保使用者模式的地址空間有效,微小過濾驅動使用ProbeForRead或ProbeForWrite這樣的例程,所有對空間的引用都在try/excetpu塊中。
延後操作的回撥函式對快速IO的處理需要保證在正確的執行緒上下文被呼叫。
延後操作的回撥函式對快速IO的處理被保證在IRQL <=APC_LEVEL上被處理,額可以安全的呼叫FltLockUserBuffer這樣的例程。
如下的程式碼演示了為目錄控制操作檢查系統空間或快速IO標誌,在需要的時候延遲完成操作的處理。
if (*DirectoryControlMdl == NULL)
{
if (FLT_IS_SYSTEM_BUFFER(CallbackData) || FLT_IS_FASTIO_OPERATION(CallbackData))
{
dirBuffer = CallbackData->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
}
else
{
// Defer processing until safe.
if (!FltDoCompletionProcessingWhenSafe(CallbackData, FltObjects, CompletionContext, Flags, Proce ssPostDirCtrlWhenSafe, &retValue))
{
CallbackData->IoStatus.Status = STATUS_UNSUCCESSFUL;
CallbackData->IoStatus.Information = 0;
}
}
}
對於既可能是基於IRP又可能是快速IO型別的操作,所有的空間引用都應該在try/execpt塊中,雖然對於那些基於IRP的緩衝區IO,不需要使用這樣的安全保護。