Windows檔案系統過濾驅動開發教程 4.裝置棧,過濾,檔案系統的感知
前邊都在介紹檔案系統驅動的結構,卻還沒講到我們的過濾驅動如何能捕獲所有發給檔案系統驅動的irp,讓我們自己來處理?前面已經解釋過了裝置對象。現在來解釋一下裝置棧。
任何裝置對象都存在於某個裝置棧中。裝置棧自然是一組裝置對象。這些裝置對象是互相關聯的,也就是說,如果得到一個DO指標,你就可以知道它所處的裝置棧。
任何來自應用的請求,最終被windows io mgr翻譯成irp的,總是發送給裝置棧的頂端那個裝置。
原始irp irp irp irp
--------------> ------> -------> ----->
DevTop Dev2 ... DevVolumne ... ???
<-------------- <------ <------- <-----
原始irp(返回) irp irp irp
向右的箭頭表示irp請求的發送過程,向左則是返回。可見irp是從裝置棧的頂端開始,逐步向下發送。DevVolumue表示我們實際要過濾的 Volume裝置,DevTop表示這個裝置棧的頂端。我們只要在這個裝置棧的頂端再綁定一個裝置,那發送給Volume的請求,自然會先發給我們的裝置 來處理。
有一個系統調用可以把我們的裝置綁定到某個裝置的裝置棧的頂端。這個調用是IoAttachDeviceToDeviceStack,這個調用2000以 及以上系統都可以用(所以說到這點,是因為還有一個IoAttachDeviceToDeviceStackSafe,是2000所沒有的。這常常導致你 的filter在2000下不能用。)
我自己寫了一個函數來幫我實現綁定功能:
//----------------------wdf.h中的內容----------------------------------
// 這個常式把源裝置綁定到目標裝置的裝置棧中去,並返回源裝置所直
// 接連結的裝置。注意源裝置未必直接綁定在目標裝置上。它應綁定在
// 目標裝置的裝置棧的頂端。
_inline wd_stat wd_dev_attach(in wd_dev *src,
in wd_dev *dst,
in out wd_dev **attached)
{
*attached = dst;
*attached = IoAttachDeviceToDeviceStack(src,dst);
if(*attached == NULL)
return wd_stat_no_such_dev;
return wd_stat_suc;
}
到這裡,我們已經知道過濾對Volume的請求的辦法。比如“C:”這個裝置,我已經知道符號串連為“C:”,不難得到裝置名稱。得到裝置名稱後,又不難得到 裝置。這時候我們IoCreateDevice()產生一個Device Object,然後調用wd_dev_attach綁定,不是一切ok嗎?所有發給“C:”的irp,就必然先發送給我們的驅動,我們也可以捕獲所有對文 件的操作了!
這確實是很簡單的處理方法。我得到的FileMon的代碼就是這樣處理的,如果不想處理動態Volume,你完全可以這樣做。但是我們這裡有更高的要 求。當你把一個隨身碟插入usb口,一個“J:”之類的Volume動態誕生的時候,我們依然要捕獲這個事件,並產生一個Device來綁定它。
一個新的儲存媒質被系統發現並在檔案系統中產生一個Volume的過程稱為Mounting.其過程開始的時候,FS的CDO將得到一個IRP,其 Major Function Code為IRP_MJ_FILE_SYSTEM_CONTROL,Minor Function Code為IRP_MN_MOUNT。換句話說,如果我們已經產生了一個裝置繫結檔案系統的CDO,那麼我們就可以得到這樣的IRP,在其中知道一個新的 Volume正在Mount.這時候我們可以執行上邊所說的操作。
現在的問題是如何知道系統中有那些檔案系統,還有就是我應該在什麼時候綁定它們的控制裝置。
IoRegisterFsRegistrationChange()是一個非常有用的系統調用。這個調用註冊一個回呼函數。當系統中有任何檔案系統被啟用或者是被登出的時候,你註冊過的回呼函數就會被調用。
//----------------------wdf.h中的內容----------------------------------
wd_stat wdff_reg_notify(
in wd_drv *driver,
in wdff_notify_func func
)
{
return IoRegisterFsRegistrationChange(driver,func);
}
你有必要為此寫一個回呼函數。
//-------------------我的回調處理函數----------------------------------
wd_void my_fs_notify(
in wd_dev *dev,
in wd_bool active)
{
wd_wchar name_buf[wd_dev_name_max_len];
wd_ustr name;
wd_ustr_init_em(&name,name_buf,wd_dev_name_max_len);
// 如果註冊了,就應該得到通知
wd_printf0("notify: a file sys have been acitved!!! \r\n");
// 得到檔案系統對象的名字,然後列印出來
wd_obj_get_name(dev,&name);
wd_printf0("notify : file sys name = %wZ\r\n",&name);
if(active)
{
wd_printf0("notify: try to attach.\r\n");
// ... 請在這裡繫結檔案系統的控制裝置
}
else
{
wd_printf0("notify: unactive.\r\n");
// ...
}
}
應該如何綁定一個檔案系統CDO?我們在下面的章節再詳細描述。
現在我們應該再在wd_main函數中加上下邊的內容:
if(wdff_reg_notify(driver,my_fs_notify) != wd_stat_suc)
{
wd_printf0("error: reg notify failed.\r\n");
wd_fio_disp_release(driver);
wd_dev_del(g_cdo);
g_cdo = wd_null;
return wd_stat_insufficient_res;
};
wd_printf0("success: reg notify ok.\n");
我們再次回顧一下,wd_main中,應該做哪些工作。
a.產生一個控制裝置。當然此前你必須給控制設定指定名稱。
b.設定Dispatch Functions.
c.設定Fast Io Functions.
d.編寫一個my_fs_notify回呼函數,在其中綁定剛啟用的FS CDO.
e.使用wdff_reg_notify調用註冊這個回呼函數。