(转载)Windows文件系统过滤驱动开发教程 联系客服

发布时间 : 星期日 文章(转载)Windows文件系统过滤驱动开发教程更新完毕开始阅读1b486c1614791711cc79175f

如何传递请求?使用IoCallDriver,该调用的第一个参数是设备对象指针,第二个参数是IRP指针。

一个IRP拥有一组IO_STACK_LOCATION.前面说过IRP在一个设备栈中传递。IO_STACK_LOCATION是和这个设备栈对应的。用于保存IRP请求在当前设备栈位置中的部分参数。如果我要把请求往下个设备传递,那么我应该把当前IO_STATCK_LOCATION复制到下一个。

我写了一些函数来处理IO_STACK_LOCATION,另外wd_irp_call用来包装IoCallDriver的功能。

//---------------------wdf.h中的内容---------------------------- typdef wd_irpsp PIO_STACK_LOCAION;

_inline wd_irpsp *wd_cur_io_stack(wd_irp *irp) {

return IoGetCurrentIrpStackLocation(irp); }

_inline wd_void wd_skip_io_stack(wd_pirp irp) {

IoSkipCurrentIrpStackLocation(irp); }

_inline wd_void wd_copy_io_stack(wd_irp *irp) {

IoCopyCurrentIrpStackLocationToNext(irp); }

_inline wd_stat wd_irp_call(wd_dev *dev,wd_pirp irp) {

return IoCallDriver(dev,irp); }

有了上边这些,我现在可以写一个默认的Dispatch Functions.

// 默认的处理很简单,忽略当前调用栈,直接发送给绑定设备 wd_stat my_disp_default(in wd_dev *dev,in wd_pirp irp) {

wd_dev *attached_dev; if(!is_my_dev(dev))

return wd_irp_failed(irp,wd_stat_invalid_dev_req); if(is_my_cdo(dev))

return wd_irp_failed(irp,wd_stat_invalid_dev_req); attached_dev = my_dev_attached(dev); if(!attached_dev)

return wd_irp_failed(irp,wd_stat_invalid_dev_req); wd_skip_io_stack(irp);

return wd_irp_call(attached_dev,irp); }

上边有一个函数is_my_dev来判断是否我的设备。这个判断过程很简单。通过dev可以得到DriverObject指针,判断一下是否我自己的驱动即可。is_my_cdo()来判断这个设备是否是我的控制设备,不要忘记在wd_main()中我们首先生成了一个本驱动的控制设备。实际这个控制设备还不做任何事情,所以对它发生的任何请求也是非法的。返回错误即可。wd_irp_failed这个函数立刻让一个irp失败。其内容如下:

// 这个函数可以立刻失败掉一个irp

_inline wd_stat wd_irp_failed(wd_pirp irp,wd_stat status_error) {

irp->IoStatus.Status = status_error; irp->IoStatus.Information = 0; return wd_irp_over(irp); }

如此一来,本不改发到我的驱动的irp,就立刻返回错误非法请求。但是实际上这种情况是很少发生的。

如果你现在想要你的驱动立刻运行,让所有的dispacth functions都调用my_disp_default.这个驱动已经可以绑定文件系统的控制设备,并输出一些调试信息。但是还没有绑定Volume.所以并不能直接监控文件读写。

对于一个绑定文件系统控制设备的设备来说,其他的请求直接调用上边的默认处理就可以了。重点需要注意的是上边曾经挂接IRP_MJ_FILE_SYSTEM_CONTROL的dispatch处理的函数my_disp_file_sys_ctl().

IRP_MJ_FILE_SYSTEM_CONTROL这个东西是IRP的主功能号。每个主功能号下一般都有次功能号。这两个东西标示一个IRP的功能。

主功能号和次功能号是IO_STACK_LOCATION的开头两字节。

//----------------我重新定义的次功能号------------------- enum {

wd_irp_mn_mount = IRP_MN_MOUNT_VOLUME,

wd_irp_mn_load_filesys = IRP_MN_LOAD_FILE_SYSTEM, wd_irp_mn_user_req = IRP_MN_USER_FS_REQUEST };

enum {

wdf_fsctl_dismount = FSCTL_DISMOUNT_VOLUME };

要得到功能号,要先得到当前的IO_STACK_LOCATION,这个上边已经有函数wd_cur_io_stack,相信这个不能难倒你。

当有Volumne被Mount或者dismount,你写的my_disp_file_sys_ctl()就被调用。具体的判断方法,就见如下的代码了:

// 可以看到分发函数中其他的函数处理都很简单,但是file_sys_ctl的 // 处理会比较复杂。我们已经在notify函数中绑定了文件系统驱动的控 // 制对象。当文件系统得到实际的介质的时候,会生成新的设备对象, // 这种设备称为卷(Volume),而这种设备是在file_sys中的mount中生 // 成的,而且也是unmount中注销掉的。我们捕获这样的操作之后,就必 // 须生成我们的设备对象,绑定在这样的“卷”上,才能绑定对这个卷 // 上的文件的操作。

wd_stat my_disp_file_sys_ctl(in wd_dev *dev,in wd_pirp irp) {

wd_dev *attached_dev;

wd_io_stack *stack = wd_cur_io_stack(irp); if(!is_my_dev(dev))

return wd_irp_failed(irp,wd_stat_invalid_dev_req); switch(wd_irpsp_minor(stack)) {

case wd_irp_mn_mount:

// 在这里,一个Volume正在Mount return my_fsctl_mount(dev,irp); case wd_irp_mn_load_filesys: return my_fsctl_load_fs(dev,irp); case wd_irp_mn_user_req: {

switch(wd_irpsp_fs_ctl_code(stack)) {

case wdf_fsctl_dismount:

// 在这里,一个Volume正dismount return my_fsctl_dismount(dev,irp); } } }

wd_skip_io_stack(irp);

attached_dev = my_dev_attached(dev); return wd_irp_call(attached_dev,irp); }

你发现你又得开始写两个新的函数,my_fsctl_mount()和my_fsctl_dismount(),来处理卷的Mount和Dismount.显然,你应该在其中生成设备或者删除,绑定或者解除绑定。很快,你就能完全监控所有的卷了。

这样做是动态监控所有的卷的完美的解决方案。

如果是在xp以上,有一个调用可以获得一个文件系统上已经被Mount的卷。但是2000下不能使用。所以我们没有使用那个方法。何况仅仅得到已经Mount的卷也不是我想要的。

这里另外还有一个my_fsctl_load_fs函数。发生于IRP_MN_LOAD_FILESYS。这个功能码我只做一点点解释:当一个文件识别器(见上文)决定加载真正的文件系统的时候,会产生一个这样的irp。

你现在可以修改你的驱动,使插入拔出u盘的时候,在Volume加载卸载时候输出调试信息。回首一下我们的脉络:

a.生成一个控制设备。当然此前你必须给控制设置指定名称。

b.设置Dispatch Functions.

c.设置Fast Io Functions.

d.编写一个my_fs_notify回调函数,在其中绑定刚激活的FS CDO.

e.使用wdff_reg_notify调用注册这个回调函数。

f.编写默认的dispatch functions.

e.处理IRP_MJ_FILE_SYSTEM_CONTROL,在其中监控Volumne的Mount和Dismount.

f.下一步自然是绑定Volumne了,请听下回分解。

7.IRP完成函数,中断级,如何超越中断级别的限制

先讨论一下Volumne设备是如何得到的.首先看以下几个函数:

// ------------------wdf.h 中的内容 ------------------------- typedef VPB wd_vpb;

_inline wd_vpb * wd_dev_vbp(wd_dev *dev)