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

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

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

9 完成讀操作

除非是一個完整的檔案系統,完成讀操作似乎是不必要的。過濾驅動一般只需要把請求交給下層的實際檔案系統來完成。但是有時候比如加解密操作,我希望從下層讀到資料,解密後,我自己來完成這一IRP請求。

這裡要談到IRP的minor function code.以前已經討論到如果major function code 是IRP_MJ_READ則是Read請求。實際上有些主功能號下面有一些子功能號,如果是IRP_MJ_READ,檢查其MINOR,應該有幾種情況: IRP_MN_NORMAL,IRP_MN_MDL,IRP_MN_MDL|IRP_COMPLETE(這個其實就是 IRP_MN_MDL_COMPLETE).還有其他幾種情況,資料上有解釋,但是我沒自己調試過,也就不胡說了。只拿自己調試過的幾種情況來說說。

先寫個函數,得到次功能號。

_inline wd_uchar wd_irpsp_minor(wd_io_stack *irpsp)
{
return irpsp->MinorFunction;
}

enum {
wd_mn_mdl = IRP_MN_MDL,
wd_mn_mdl_comp = IRP_MN_MDL_COMPLETE,
wd_mn_normal = IRP_MN_NORMAL
};

希望你喜歡我重新定義過的次功能號碼。

wd_mn_normal的情況完全與上一節同(讀者可能要罵了,上一節明明說就是這樣的,結果還有其他的情況,那不是騙人嗎?但是微軟的東西例外就是多,我也實在拿他沒辦法... ).

注意如上節所敘述,wd_mn_normal的情況,既有可能是在Irp->MdlAddress中返回資料,也可能是在Irp->UserBuffer中返回資料,這個取決於Device的標誌.

但是如果次功能號為wd_mn_mdl則完全不是這個意思。這種irp一進來看資料,就赫然發現Irp->MdlAddress和Irp->UserBuffer都為空白。那你得到資料後把資料往哪裡拷貝呢?

wd_mn_mdl的意思是請自己分配一個mdl,然後把mdl指向你的資料所在的空間,然後返回給上層。自然mdl是要釋放的,換句話說事業使用完畢要歸還,所以又有wd_mn_mdl_comp,意思是一個mdl已經使用完畢,可以釋放了。

mdl用於描述記憶體的位置。據說和NDIS_BUFFER用的是同一個結構。這裡不深究,我寫一些函數來分配和釋放mdl,並把mdl指向記憶體位置或者得到mdl所指向的記憶體:

// 釋放mdl
_inline wd_void wd_mdl_free(wd_mdl *mdl)
{
IoFreeMdl(mdl);
}

// 這個這個東西分配mdl,緩衝必須是非分頁的。可以在dispatch level跑。
_inline wd_mdl *wd_mdl_alloc(wd_void* buf,
wd_ulong length)
{
wd_mdl * pmdl = IoAllocateMdl(buf,length,wd_false,wd_false,NULL);
if(pmdl == NULL)
return NULL;
MmBuildMdlForNonPagedPool(pmdl);
return pmdl;
}

// 這個函數分配一個mdl,並且帶有一片記憶體
_inline wd_mdl *wd_mdl_malloc(wd_ulong length)
{
wd_mdl *mdl;
wd_void *point = wd_malloc(wd_false,length);
if(point == NULL)
return NULL;
mdl = wd_mdl_alloc(point,length);
if(mdl == NULL)
{
wd_free(point);
return NULL;
}
return mdl;
}

// 這個函數釋放mdl並釋放mdl所帶的記憶體。
_inline wd_void wd_mdl_mfree(wd_mdl *mdl)
{
wd_void *point = wd_mdl_vaddr(mdl);
wd_mdl_free(mdl);
wd_free(point);
}

// 得到地址。如果想往一個MdlAddress裡邊拷貝資料 ...
_inline wd_void *wd_mdl_vaddr(wd_mdl *mdl)
{
return MmGetSystemAddressForMdlSafe(mdl,NormalPagePriority);
}

另外我通過這兩個函數來設定和擷取Irp上的mdl.

_inline wd_mdl *wd_irp_mdl(wd_irp *irp)
{
return irp->MdlAddress;
}

_inline wd_void wd_irp_mdl_set(wd_irp *irp,
wd_mdl *mdl)
{
irp->MdlAddress = mdl;
}

一個函數獲得UserBuffer.
_inline wd_void * wd_irp_user_buf(wd_irp *irp)
{
return irp->UserBuffer;
}

要完成請求還有一個問題。就是irp->IoStatus.Information.在這裡你必須填上實際讀取得到的位元組數字。不然上層不知道有多 少資料返回。這個數字不一定與你的請求的長度等同(其實我認為幾乎只要是成功,就應該都是等同的,唯一的例外是讀取到檔案結束的地方,長度不夠了的情 況)。我用下邊兩個函數來擷取和設定這個數值:

_inline wd_void wd_irp_infor_set(wd_irp *irp,
wd_ulong infor)
{
irp->IoStatus.Information = infor;
}

_inline wd_ulong wd_irp_infor(wd_irp *irp)
{
return irp->IoStatus.Information;
}

也許你都煩了,但是還有事情要做。作為讀檔案的情況,如果你是自己完成請求,不能忘記移動一下檔案指標。否則作業系統會不知道檔案指標移動了而反覆讀同一個地方永遠找不到檔案尾,我碰到過這樣的情況。

一般是這樣的,如果檔案讀取失敗,請保持原來的檔案指標位置不要變。如果檔案讀取成功,請把檔案指標指到“讀請求位移量+成功讀取長度”的位置。

這個所謂的指標是指Irp->FileObject->CurrentByteOffset.

我跟蹤過正常的windows檔案系統的讀行為,我認為並不一定是向我上邊說的這樣做。情況很複雜,有時動,有時不動(說複雜當然是因為我不理解),但是按我上邊說的方法來完成,我還沒有發現過錯誤。

我用以下函數來設定和擷取這個。

_inline wd_void wd_file_offset_add(wd_file *file,wd_ulong len)
{
file->CurrentByteOffset.QuadPart += len;
}

_inline wd_void wd_file_offset_set(wd_file *file,wd_lgint offset)
{
file->CurrentByteOffset = offset;
}

_inline wd_lgint wd_file_offset(wd_file *file)
{
return file->CurrentByteOffset;
}

現在看看怎麼完成這些請求,假設我已經有資料了。現在假設本裝置緩衝標記為0,即正常情況採用Irp->UserBuffer返回資料. 這些當然都是在my_disp_read中或者是其他想完成這個irp的地方做的(希望你還記得我們是如何來到這裡),假設其他必要的判斷都已經做了:

wd_irpsp *irpsp = wd_irp_cur_io_stack(irp);
switch(wd_irpsp_minor(irpsp))
{
// 我先保留檔案的位移位置
case wd_mn_normal:
{
wd_void *point = wd_irp_user_buf(irp);

// 如果有資料,就往point ...裡邊拷貝 ...

wd_irp_infor_set(irp,length);
wd_irp_status_set(irp,wd_suc);
wd_file_offset_set(wd_irp_file(irp),offset+length);

return wd_irp_over(irp);
}
case wd_mn_mdl:
{
wd_void *mdl = wd_mdl_malloc(length); // 情況比上邊的複雜,請先分配mdl
if(mdl == NULL)
{
// ... 返回資源不足 ...
}

wd_irp_mdl_set(irp,mdl);
wd_irp_infor_set(irp,length);
wd_irp_status_set(irp,wd_suc);

wd_file_offset_set(wd_irp_file(irp),offset+length);

return wd_irp_over(irp);

}
case wd_mn_mdl_comp:
{
// 沒有其他任務,就是釋放mdl
wd_mdl_mfree(wd_irp_mdl(irp));
wd_irp_mdl_set(irp,0);
wd_irp_infor_set(irp,0);
wd_irp_status_set(irp,wd_status_suc);

return wd_irp_over(irp);
}
default:
{
// 我認為其他的情況不過濾比較簡單 ...
}
}

重要提醒:wd_mn_mdl的情況,需要分配一個mdl,並且這個mdl所帶有的記憶體是有一定長度的,這個長度必須與後來的irp-> IoStatus.Information相同!似乎上層並不以irp->IoStatus.Information返回的長度為準。比如明明唯讀 了50個位元組,但是你返回了一個mdl指向記憶體長度為60位元組,則作業系統則認為已經讀了60個位元組!這非常糟糕。

最後提一下檔案是如何結尾的。如果到某一處,返回成功,但是實際讀取到的資料沒有請求的資料長,這時還是返回wd_status_suc (STATUS_SUCCESS),但是此後作業系統會馬上發irp來讀最後一個位置,此時返回長度為0,返回狀態STATUS_FILE_END即可。

已經費巨大篇幅解釋讀請求。我不會再講解寫請求了。相信讀者有能力自己搞清楚。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.