Windows文件系统过滤驱动开发教程(10)

Windows文件系统过滤驱动开发教程

注: 有任何问题与建议请加QQ16191935,邮箱MFC_Tan_Wen@163.com

工作忙,好久没有来过了,请大家谅解。

10 自己发送Irp完成读请求

关于这个有一篇文档解释得很详细,不过我认为示例的代码有点太简略了,这篇文档在IFS所附带的OSR文档中,请自己寻找。

为何要自己发送Irp?在一个文件过滤驱动中,如果你打算读写文件,可以试用ZwReadFile.但是这有一些问题。Zw系列的Native API使

用句柄。而句柄是有线程环境限制的。此外也有中断级别的限制。再说,Zw系列函数来读写文件,最终还是要发出Irp,又会被自己的过滤驱动

捕获到。结果带来判断上的困难。对资源也是浪费。那么最应该的办法是什么呢?当然是直接对卷设备发Irp了。

但是Irp是非常复杂的数据结构,而且又被微软所构造的很多未空开的部件所处理。所以自己发irp并不是一件简单的事情。

比较万能的方法是IoAllocateIrp,分配后自己逐个填写。问题是细节实在太多,很多无文档可寻。有兴趣的应该看看我上边所提及的

那篇文章“Rolling Your Own”。

有意思的是这篇文章后来提到了捷径,就是利用三个函数:

IoBuildAsynchronousFsdRequest(...)
IoBuildSynchronousFsdRequest(...)
IoBuildDeviceIoControlRequest(...)

于是我参考了他这方面的示例代码,发现运行良好,程序也很简单。建议怕深入研究的选手就可以使用我下边提供的方法了。

首先的建议是使用IoBuildAsynchronousFsdRequest(),而不要使用同步的那个。使用异步的Irp使irp和线程无关。而你的过滤驱动一

般很难把握当前线程(如果你开一个系统线程来专门读取文件那例外)。此时,你可以轻松的在Irp的完成函数中删除你分配过的Irp,避免去追

究和线程相关的事情。

但是这个方法有局限性。文档指出,这个方法仅仅能用于IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_FLUSH_BUFFERS,和IRP_MJ_SHUTDOWN.

刚好我这里仅仅要求完成文件读。

用Irp完成文件读需要一个FILE_OBJECT.FileObject是比Zw系列所用的句柄更好的东西。因为这个FileObject是和线程无关的。你可以

放心的在未知的线程中使用他。

自己要获得一个FILE_OBJECT必须自己发送IRP_MJ_CREATE的IRP.这又不是一件轻松的事情。不过我跳过了这个问题。因为我是文件系

统过滤驱动,所以我从上面发来的IRP中得到FILE_OBJECT,然后再构造自己的IRP使用这个FILE_OBJECT,我发现运行很好。

但是又出现一个问题,如果IRP的irp->Flags中有IRP_PAGING(或者说是Cache管理器所发来的IRP)标记,则其FileObject我获得并使

用后,老是返回错误。阅读网上的经验表明,带有IRP_PAGINGE的FileObject不可以使用.于是我避免使用这时的FileObject.我总是使用不带

IRP_PAGING的Irp(认为是用户程序发来的读请求)的FileObject。

好,现在废话很多了,现在来看看构造irp的代码:

_inline wd_irp *wd_irp_fsd_read_alloc(wd_dev *dev,
wd_void *buf,
wd_ulong length,
wd_lgint *offset,
wd_io_stat_block *io_stat)
{
return IoBuildAsynchronousFsdRequest(IRP_MJ_READ,dev,
buf,length,
offset,
io_stat);
}

io_stat我不知道是做何用,我一般填写NULL.类型是PIO_STATUS_BLOCK.buf则是缓冲。在Irp中被用做UserBuffer接收数据。offset是

这次读的偏移量。

以上函数构造一个读irp.请注意,此时您还没有设置FileObject.实际上我是这样发出请求的:

irp = wd_irp_fsd_read_alloc(dev,buf,len,&start,NULL);
if(irp == NULL)
return;
irpsp = wd_irp_next_sp(irp);
wd_irpsp_file_set(irpsp,file);
wd_irp_comp(irp,my_req_comp,context);

请注意wd_irp_next_sp,我是得到了这个irp的下一个IO_STACK_LOCATION,然后我设置了FileObject.接下来应该设置了完成后,我就

可以发送请求了!请求发送完毕后,一旦系统完成请求就会调用你的my_req_comp.

再看看my_req_comp如何收场:

wd_stat my_req_comp(in wd_dev *dev,
in wd_irp *irp,
in wd_void *context)
{

// 请注意,无论成功还是失败,我都得在这里彻底销毁irp
wd_irp_send_by_myself_free(irp);

// 返回这个,仅仅是因为irp我已经销毁,不想让系统再次销毁它而已。
return wd_stat_more_processing;
}

wd_stat_more_prcessing就是STATUS_MORE_PROCESSING_REQUIRED。之所以返回这个,是因为我已经把irp给删除了。我不想windows系

统再对这个irp做什么。所以干脆说我还要继续处理,这样避免了io管理器再去动用这个irp的可能。

最后是那个irp删除函数的代码:

_inline wd_void wd_irp_send_by_myself_free(wd_irp *irp)
{
if (irp->MdlAddress)
{
MmUnmapLockedPages(MmGetSystemAddressForMdl(irp->MdlAddress),
irp->MdlAddress);
MmUnlockPages(irp->MdlAddress);
IoFreeMdl(irp->MdlAddress);
}
IoFreeIrp(irp);
};

因为我自己没有分配过MdlAddress下去过。所以如果下边返回了MDL,我得附带清理它。

好了,祝愿你心情愉快。如果你何我一样懒惰的话,不妨就用上边的简单方法自己发irp来读文件了。
原文地址:https://www.cnblogs.com/jasononline/p/1231775.html