Windows檔案系統過濾驅動開發教程(8)

來源:互聯網
上載者:User
Windows檔案系統過濾驅動開發教程

注: 有任何問題與建議請加QQ16191935,郵箱MFC_Tan_Wen@163.com

8 終於綁定了Volume,讀操作的捕獲與分析

上文已經講到綁定Volume之前的關鍵操作.我們一路逢山開路,逢水架橋,相信你從中也學到了驅動開發的基本方法.以後的工作,無非靈活運用這些方法而 已.前邊已經舉出過綁定FS CDO的操作.那麼現在綁定Volume,無非照貓畫虎,而以後的教程中,我也不會逐一詳盡的列舉出細節的代碼了.

但是綁定Volume的過程中,還是有些地方需要稍微注意:

1.獲得Storage Device Object的問題.前邊已經說過,為了得到Vbp,我要先得到Storage Device Object,方法如下:

wd_irpsp *irpsp = wd_cur_io_stack(irp);
wd_dev *storage_dev = wd_irpsp_mount_storage(irpsp);

這是在Irp完成之前,這樣調用是對的.但是到完成函數中,情況就不同了.因為這個irpsp下儲存的storage_dev可能被修改掉(這並非我自己 調試的結果,而是閱讀sfilter代碼中的注釋而得到的資訊).既然有這個可能,我們只好先把storage_dev這個東西儲存下來.實際上,可以在 Device擴充增加一個項目storage_dev.在irp完成之前,產生我要連結的裝置(只是不綁定),並把這個裝置指標傳入完成函數上下文.

這樣在完成函數中,我就能得到這個storage_dev,最終找到Volmue裝置的指標,此時進行綁定就可以了.

2.綁定的過程,未必一次就能成功.因為Volmue裝置的Initilize標記被清除之前,我的綁定是不能成功的.對這種情況,我抄襲了sfilter中的方法,就是延時500毫秒,然後再嘗試.一共嘗試8次.

我封裝了一個函數用來延時:

_inline wd_void wd_delay_milli_se(wd_ulong milli_sec)
{
wd_llong delay = milli_sec*(-10)*1000;
wd_lgint interval;
interval.QuadPart = delay;
KeDelayExecutionThread(KernelMode,FALSE,&interval);
}

這個函數的參數是毫秒,但是我並不清楚有多小的精度.

其他的就不說了,祝願你的運氣足夠的好.現在我們處理IRP_MJ_READ,如果你已經綁定了Volume,那麼顯然,發送給Volume的請求就會先發送給你.處理IRP_MJ_READ,能捕獲檔案的讀操作.

進入你的my_disp_read()函數(假設你註冊了這個函數來處理IRP_MJ_READ,請見前面關於分發函數的講述),首先判斷這個Dev是不是綁定Volume的裝置.如果是,那麼就是一個讀檔案的操作.

如何判斷?記得我們先綁定Volume的時候,在我們的裝置擴充中設定了storage_dev,如果不是(比如是FS CDO,我們沒設定過),那麼這麼判斷即可:

if(is_my_dev(dev))
{
my_dev_ext *ext = (my_dev_ext *)wd_dev_ext(dev);
if(ext->storage_dev)
{
// ... 到這裡說明是對檔案的讀操作
}
}

其他的情況不需要捕獲,請直接傳遞到下層.

讀請求的IRP情況非常複雜,請有足夠的心理準備.並不要過於依賴協助,最好的辦法就是自己列印IRP的各個細節,親自查看檔案讀操作的完成過程.而我現在所說的東西,換另一我未嘗試過的版本的windows是否還正確,我也無法下斷言.

不過基本的東西是不會變的.

首先關心被讀寫的檔案.IRP下有一個FileObject指標.這個東西指向一個檔案對象.你可以得到檔案對象的名字,這個名字是沒有盤符的檔案全路 徑.有人要問那麼盤符如何獲得?因為你已經知道了Volume,前邊已經說過盤符不過是Volume的符號串連名,那麼想要真正的全路徑問題應該也不大 了.

我現在偷一個懶,我現在只列印沒有盤符的路徑名.先寫一個函數,從IRP得到FileObject.

_inline wd_file *wd_irp_file(wd_irpsp *irpsp)
{
return irpsp->FileObject;
}

然後寫一個函數來獲得檔案名稱.這個函數參考的是FileMon的代碼.

wd_void wd_file_get_name(in wd_file *file,
in out wd_ustr *name)
{
if( file->FileName.Buffer &&
!(file->Flags & FO_DIRECT_DEVICE_OPEN) )
RtlCopyUnicodeString(name,&file->FileName);
}

接下來有必要得到讀檔案的位移量.和vxd的檔案系統驅動不同,2000下檔案系統得到的位移量似乎都是從檔案起始位置開始計算的.位移量是一個LARGE_INTEGER.因為現在確實有些檔案已經超過了長整型所能表示的大小.

以下函數用來得到位移量.wd_lgint是經過重定義的LARGE_INTEGER.

_inline wd_lgint wd_irp_read_offset(wd_irpsp *irpsp)
{
return irpsp->Parameters.Read.ByteOffset;
}

注意以上的參數不是irp.是當前IO_STACK_LOCATION,也就是我的wd_irpsp.前面已經講述過如何擷取當前irpsp.

此外我還希望能得到我所讀到的資料.這要注意,我們捕獲這個請求的時候,這個請求還沒有完成.既然沒有完成,當然無資料可讀.如果要擷取,那就設定完成函數,在完成函數中完成請求.

完成Irp的時候忽略還是拷貝當前IO_STACK_LOCATION,返回什麼STATUS,以及完成函數中如何結束Irp,是不那麼容易搞清楚的一件事情.我想做個總結如下:

1.如果對irp完成之後的事情沒有興趣,直接忽略當前IO_STACK_LOCATION,(對我的程式來說,調用wd_ship_cur_io_stack),然後向下傳遞請求,返回wd_irp_call()所返回的狀態.

2.不但對irp完成之後的事情沒有興趣,而且我不打算繼續傳遞,打算立刻返回成功或失敗.那麼我不用忽略或者拷貝當前IO_STACK_LOCATION,填寫參數後調用IoCompleteRequest,並返回我想返回的結果.

3.如果對irp完成之後的事情有興趣,並打算在完成函數中處理,應該首先拷貝當前IO_STACK_LOCATION (wd_copy_cur_io_stack()),然後指定完成函數,並返回wd_irp_call()所返回的status.完成函數中,不需要調用 IoCompleteRequest!直接返回Irp的目前狀態即可.

4.同3的情況,有時候,會把任務塞入系統工作者線程或者希望在另外的線程中去完成Irp,那麼完成函數中應該返回 wd_stat_more_processing,此時完成Irp的時候應該調用IoCompleteRequest.另一種類似的情況是在 dispatch函數中等待完成函數中設定事件,那麼完成函數返回wd_stat_more_processing,dispatch函數在等待結束後調 用IoCompleteRequest.

前邊已經提到過裝置的DO_BUFFERED_IO,DO_DIRECT_IO這兩個標記.情況是3種:要麼是兩個標記中其中一個,要麼是一個都沒有. Volume裝置出現DO_BUFFERED的情況幾乎沒有,我碰到的都是一個標記都沒有.DO_DIRECT_IO表示資料應該返回到Irp-> MdlAddress所指向的MDL所指向的記憶體.在無標記的情況下,表明資料讀好,請返回到
Irp->UseBuffer中即可.

UseBuffer是一個只在當前線程上下文才有效地址.如果你打算按這個地址獲得資料,你最好在當前線程上下文中.完成函數與 my_disp_read並非同一個線程.所以在完成函數中按這個地址去擷取資料是不對的.如何回到當前線程?我採用簡單的辦法.在 my_disp_read中設定一個事件,調用wd_irp_call(即ddk中的IoCallDriver)之後開始等待這個事件.而在完成函數中設 置這個事件.這樣等待結束的時候,剛好Irp已經完成,我也回到了我的my_disp_read原來的線程.

wd_stat my_disp_read(in wd_dev *dev,in wd_pirp irp)
{
my_dev_ext *ext;
wd_dev *attached_dev;
wd_irpsp *irpsp = wd_cur_io_stack(irp);
wd_stat status;
wd_file *file = wd_irp_file(irpsp);
wd_lgint offset = wd_irp_read_offset(irpsp);
wd_size length = wd_irp_read_length(irpsp);
wd_wchar name_buf[512];
wd_ustr name;
wd_event event;

// 檢查是否我的裝置
if(!is_my_dev(dev))
return wd_irp_failed(irp,wd_stat_invalid_dev_req);

ext = (wdff_dev_ext *)wd_dev_ext(dev);
attached_dev = wdff_dev_attached(dev);

// 到這裡判斷得到這是對一個被綁定了的卷的讀操作
if(ext->storage_dev == NULL)
{
wd_skip_io_stack(irp);
return wd_irp_call(attached_dev,irp);
}

// 到了這裡,確認是對檔案的讀
wd_ustr_init_em(&name,name_buf,512);
if(file)
wd_file_get_name((wd_void *)file,&name);
else
{
wd_skip_io_stack(irp);
return wd_irp_call(attached_dev,irp);
}

wd_printf1("xxx irp flag = %x\r\n",wd_irp_flags(irp));
wd_printf1("xxx file read: %wZ \r\n",&name);
wd_printf1("xxx read offset = %ld ",offset);
wd_printf1("xxx read length = %ld\r\n",length);

// 以上我已經列印了讀請求的相關參數,下面我希望得到讀出的內容
wd_event_init(&event);
// 先拷貝當前io_stack,然後再指定完成常式

wd_copy_io_stack(irp);
wd_irp_set_comp(irp,my_disp_read_comp,&event);

// 對實際裝置呼叫irp
status = wd_irp_call(attached_dev,irp);
if(status == wd_stat_pending)
wd_event_wait(&event);

wd_printf1("test read end:status = %x \r\n",status);
// 如果此時status = 0,那麼內容應該就在Irp->UserBuffer中,請自己列印...
wd_printf1("test read end:read length = %ld\r\n",wd_irp_infor(irp));

return wd_irp_over(irp);
}

然後是my_disp_read_comp的內容,可以看見只是簡單的設定事件,然後返回wd_stat_more_processing.

wd_stat my_disp_read_comp(in wd_dev *dev,
in wd_irp *irp,
in wd_void *context)
{
wd_event * event=
(wd_event *)context;
UNREFERENCED_PARAMETER(dev);
UNREFERENCED_PARAMETER(irp);
wd_event_set(event);
return wd_stat_more_processing;
}

儘管已經寫了很多,儘管我們得到了讀過程的所有參數和結果,我們依然不知道如果自己寫一個檔案系統,該如何完成讀請求,或者過濾驅動中,如何修改讀請求等等.我們下一節繼續討論讀操作.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.