?? wdf源代碼1.txt
字號(hào):
文件過濾系統(tǒng)設(shè)計(jì)(1) (附全部源代碼)
本文作者為上海楚狂人,有意見和建議者請(qǐng)聯(lián)系(QQ16191935,MSN walled_river@hotmail.com)
文件過濾系統(tǒng)設(shè)計(jì)(1) 友好外部接口的lib
首先說明一下,此文所附的代碼,也就是<<Windows文件系統(tǒng)過濾驅(qū)動(dòng)開發(fā)教程>>的代碼。閱讀此文需要預(yù)先了解文件過濾驅(qū)動(dòng),最好讀過<<Windows文件系統(tǒng)過濾驅(qū)動(dòng)開發(fā)教程>>。我計(jì)劃再發(fā)一系列文章總結(jié)這方面的最新積累的經(jīng)驗(yàn),當(dāng)然我不敢取名“教程”,因?yàn)槲覜]有資格教育任何人。
我不幸被卷入數(shù)個(gè)與文件過濾有關(guān)的項(xiàng)目中,以致為此耗費(fèi)了數(shù)年的時(shí)光.我期望我的設(shè)計(jì)趨于理想,把我的編碼工作引入美好的狀態(tài).
從理想的角度來說,文件過濾是與操作系統(tǒng)無關(guān)的.因?yàn)槭紫任易鑫募^濾的時(shí)候并未關(guān)心文件系統(tǒng)的類型.FAT32還是NTFS? 如果機(jī)器上有其他文件系統(tǒng),我也打算過濾它.然后,操作系統(tǒng)并未與文件系統(tǒng)相關(guān).Windows上也可以安裝ext2(聽聞已經(jīng)有人開發(fā)了相關(guān)的驅(qū)動(dòng)),linux上安裝FAT32與NTFS(NTFS的驅(qū)動(dòng)可能尚不完美)已經(jīng)是事實(shí).
提煉與操作系統(tǒng)無關(guān)的文件操作是一個(gè)比較復(fù)雜的接口設(shè)計(jì)工作.能碰到千奇百怪的困難.所以我認(rèn)為,應(yīng)該反過來入手,首先精簡Windows的文件操作的接口,再在這個(gè)基礎(chǔ)上追求操作系統(tǒng)無關(guān)化.當(dāng)然你也可以認(rèn)為操作系統(tǒng)無關(guān)是完全不可能的,這個(gè)目標(biāo)是錯(cuò)誤的.但是我已經(jīng)精簡了文件系統(tǒng)過濾操作的接口,使之后的編程工作變得簡單.
我把這個(gè)項(xiàng)目稱為FSFE(File System Filter Engine),這是一個(gè)非商業(yè)化的項(xiàng)目,所有代碼都是本人利用工作業(yè)余時(shí)間編寫.你可以下載這些代碼并修改使用,但是不能用于商業(yè)開發(fā)中.
在Windows下研究文件過濾一般都從SFilter入手.SFilter可以看作一個(gè)文件過濾和Windows的文件系統(tǒng)之間的一個(gè)"適配器",但絕對(duì)不是你要開發(fā)的文件過濾系統(tǒng)本身.既然Windows的文件系統(tǒng)操作以IRP作為單元,并在dispatch functions中處理IRP,那么你的文件過濾只需要考慮過濾IRP就可以了.又何必去考慮哪些文件系統(tǒng)何時(shí)激活,又合適加載了卷,何時(shí)卷被卸載這些無關(guān)的問題呢?
我希望我的編碼不需要考慮那些問題,而是直接如此編碼(為了避免更多的板磚我使用盡量DDK原裝的數(shù)據(jù)類型):
// 這是我的主函數(shù)
void fsfe_main()
{
// 我為create irp的處理設(shè)置一個(gè)回調(diào)函數(shù)
fsfe_set_create(my_create);
// 我為close 和 clean up的irp處理設(shè)置一個(gè)回調(diào)函數(shù)
fsfe_set_close_clean_up(my_close_clean_up);
}
以上是我打算過濾create和close的情況.create的過濾一般用來阻止某些文件被訪問,或者"重新定向"對(duì)某些文件的訪問(比如把文件操作映射到另外一個(gè)磁盤或目錄中).
我相信計(jì)算機(jī)中所有的文件的create irp都會(huì)發(fā)到我這里.這時(shí)我就可以編寫我的過濾了:
ULONG
my_create(
IN PDEVICE_OBJECT cur, // 當(dāng)前設(shè)備
IN PDEVICE_OBJECT next, // 下一個(gè)設(shè)備
IN PIRP irp, // irp
OUT NTSTATUS status, // 如果irp被完成,狀態(tài)返回到這里
OUT PVOID *context) // 可以為完成函數(shù)指定一個(gè)上下文
{
// create傳入的參數(shù)我并不一定都要使用.如果我要打印請(qǐng)求生成文件
// 的路徑,只要這樣這就可以了:
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
PFILE_OBJECT file = irpsp->FileObject;
if(file != NULL)
{
DbgPrint("CREATE %wZ\r\n", &file->FileName);
}
// 自己定義的一個(gè)常量.意義為"繼續(xù)完成",對(duì)IRP我不干涉。
return WDFF_CB_GO_ON;
}
irpsp->FileObject->FileName只是一個(gè)請(qǐng)求路徑。直接用這個(gè)路徑用來做路徑過濾(比如限制對(duì)某個(gè)目錄下的文件訪問)是不現(xiàn)實(shí)的。首先這個(gè)路徑可能不是絕對(duì)路徑,可能只是某個(gè)目錄的相對(duì)路徑,而且不含有盤符。更糟的是可能出現(xiàn)短名,如mypath~1之類,讓你的過濾落空。后面我們專門研究如何做路徑過濾。但是這個(gè)演示還是不錯(cuò)的。
假設(shè)你手頭有Sfilter的代碼,那么你可以按如下的解釋實(shí)現(xiàn)以上的功能.
首先Sfilter將被編譯成一個(gè)Lib.使用Lib使你或者以你的成果為基礎(chǔ)的開發(fā)者不必再關(guān)心Sfilter的代碼.
以上帶來的一個(gè)擔(dān)心是調(diào)試BUG的問題.但是當(dāng)然需要調(diào)試的時(shí)候,你會(huì)發(fā)現(xiàn)有代碼的lib調(diào)試起來和你常用的全代碼的情況一樣的方便.
令一個(gè)容易被誤會(huì)的問題常常是lib的大小的問題.一般的lib編譯出來(尤其是功能非常豐富的lib)編譯出來都非常的大.其實(shí)連接一個(gè)lib絕對(duì)不等于把這個(gè)lib的全部大小放到你的驅(qū)動(dòng)中.連接器只連接必要的部分.你未使用到的部分會(huì)被剔除.因此你會(huì)發(fā)現(xiàn)你連接的結(jié)果往往比lib本身要小很多.
修改SFilter目錄下的SOURCE文件.把
TARGETTYPE=DRIVER
修改為
TARGETTYPE=LIBRARY
然后我可以在SFilter.c中,DriverEntry的實(shí)現(xiàn)之前,加入
extern void fsfe_main(); // 外部函數(shù)聲明
然后在DriverEntry中,合適的位置調(diào)用:
NT_STATUS DriverEntry(...)
{
...
// 可以把fsfe_main設(shè)計(jì)成接受一些參數(shù),但是我沒有
// 用這樣的設(shè)計(jì).
fsfe_main( ... );
... // 繼續(xù)初始化
}
這樣你的代碼中不必再有DriverEntry,但是必須有fsfe_main.當(dāng)然你必須連接這個(gè)庫.在你的SOURCE文件中,加上:
TARGETLIBS = sfilter.lib
check版本和free版本需要不同的庫.你可以像這樣:
!IF "$(DDKBUILDENV)"=="fre"
TARGETLIBS = sfilter.lib
!else
TARGETLIBS = sfilterd.lib
!ENDIF
以上是編譯問題.下面考慮分發(fā)函數(shù).實(shí)際上與fsfe_main的嵌入相同.但是為了這些函數(shù)不是固定名字,而是可以設(shè)置的,我必須在SFilter中定義一組函數(shù)指針:
一個(gè)IRP處理的過濾函數(shù)類型:
typedef LONG
(*wdff_callback_pre_func)(
IN PDEVICE_OBJECT cur, // 當(dāng)前設(shè)備
IN PDEVICE_OBJECT next, // 下一個(gè)設(shè)備
IN PIRP irp, // irp
OUT NTSTATUS status, // 如果irp被完成,狀態(tài)返回到這里
OUT PVOID *context);
一個(gè)IRP完成的過濾函數(shù):
typedef LONG
(*wdff_callback_post_func)(
IN PDEVICE_OBJECT cur,
IN PDEVICE_OBJECT dev,
IN PIRP irp,
IN PVOID context);
// 一個(gè)含有一組回調(diào)指針的結(jié)構(gòu),有點(diǎn)類似linux驅(qū)動(dòng)開發(fā)的分發(fā)函數(shù)指針數(shù)組。
typedef struct _wdff_callback
{
wdff_callback_pre_func read_write;
wdff_callback_post_func read_write_comp;
wdff_callback_pre_func create;
wdff_callback_post_func create_comp;
wdff_callback_pre_func close_clean_up;
wdff_callback_post_func close_clean_up_comp;
wdff_callback_pre_func device_io_ctrl;
wdff_callback_post_func device_io_ctrl_comp;
wdff_callback_pre_func other;
wdff_callback_post_func other_comp;
} wdff_callback;
我在這中間做了一些簡化的處理.比如常用的IRP被單獨(dú)提出來作為函數(shù)指針.而"其他"的IRP處理則被集合到一個(gè)叫做other的處理函數(shù)中.
現(xiàn)在在SFilter中定義一個(gè)如下的變量:
wdff_callback g_callback;
我很容易提供接口來設(shè)置它.我可以在庫中導(dǎo)出一個(gè)函數(shù):
wd_void fsfe_set_create(wdff_callback_pre_func create)
{
g_callback.create = create;
};
其他的接口都是類似的.下面的問題,就是在SFilter中何時(shí)調(diào)用這個(gè)回調(diào)函數(shù)了.相信這個(gè)難不到對(duì)SFilter了解的諸位:
NT_STATUS SfCreate(...)
{
...
if(g_callback.create != NULL)
{
(g_callback.create)( ...)
}
}
但是具體到我給出的代碼中,情況有一些不同.但是都是細(xì)節(jié)問題,有興趣的讀者可以自己研究.我提供的代碼并不是SFilter修改而來,而是我自己編寫的過濾框架。比SFilter的優(yōu)點(diǎn)在于,提供了路徑過濾,影設(shè)備讀寫,2000下動(dòng)態(tài)加載卸載等一些新功能。
所附代碼有如下的說明:
0.使用這些代碼到你的計(jì)算機(jī)上,一切后果自負(fù)。
1.編譯必須安裝DDK 3790或者以上的版本,用WNET編譯。必須定義環(huán)境變量DDKROOT或者BASEDIR.
2.得到代碼后解壓。用VC6.0或者VC7.0打開工作空間后點(diǎn)Build,Rebuild All或批構(gòu)建可直接編譯之。編譯方法與編譯應(yīng)用程序同。不要試圖用ddk或者ds提供的工具去編譯它,那樣會(huì)碰到一些麻煩。如果失敗請(qǐng)檢查環(huán)境變量。
3.編譯后得到的lib在lib目錄下,主要有wdf.lib和fsfe.lib.需要的頭文件在inc目錄下。
4.使用這些lib的例子在Sample目錄下。這些目錄下的工程也會(huì)在工作空間全部編譯的時(shí)候被編譯。得到的sys每一個(gè)都可以在2k-2003中所有的系統(tǒng)上動(dòng)態(tài)加載和卸載。加載后打開DbgView察看輸出即可。目前只有一個(gè)例子 create_filter.我會(huì)在后面的系列文章中編寫更多的例子。create_filter.sys加載后會(huì)打印出系統(tǒng)當(dāng)前打開的文件路徑(帶盤符)。
5.如果出現(xiàn)了蘭屏,死機(jī)或者其他意外,請(qǐng)不要過于憤怒。如果你能修改程序的bug,希望你發(fā)一份給我指出我的錯(cuò)誤,我非常感謝。
值得指出的是,有時(shí)DbgPrint似乎會(huì)導(dǎo)致藍(lán)屏。在正式發(fā)布的商業(yè)軟件中不應(yīng)該包含DbgPrint調(diào)用。我目前包含了許多。這是為了作為演示。
其他方面的問題將在后續(xù)的文章中繼續(xù)說明。
描述:Wdf源代碼
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -