Windows 虚拟磁盘驱动开发(采用原始办法实现类似Storport框架的相同功能)

Posted 雨中风华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows 虚拟磁盘驱动开发(采用原始办法实现类似Storport框架的相同功能)相关的知识,希望对你有一定的参考价值。

其实以前讲述windows平台下的磁盘驱动的开发挺多,而且时间也是非常早。

以下连接:

https://blog.csdn.net/fanxiushu/article/details/9903123?spm=1001.2014.3001.5501

https://blog.csdn.net/fanxiushu/article/details/11713357?spm=1001.2014.3001.5501

讲述的主要就是以 Storport 和 Scsiport 框架为主的windows平台的虚拟磁盘驱动开发,

并且在CSDN上提供了一个基于这两个框架的例子代码:

https://download.csdn.net/download/fanxiushu/5994583?spm=1001.2014.3001.5501

看看写博客的时间,距今快10年了,一个非常难言而又漫长的岁月(相对日新月异的信息技术而言)

然而仔细分析却会发现,其核心和本质的东西,并没改变多少,基本保持着多年前的哪些框架和原理。

除非在信息技术和操作系统领域发生了颠覆性的革命(比如以生物计算代替硅基电路),

否则经过人们多年努力建立起来的基础核心,都不会有太大变化。

比如现在的电脑,各种终端,服务器等都遵循着 冯·诺依曼型计算机的基本规则。

回到我们的windows虚拟磁盘驱动。

为何想到要用原始办法来开发类似Storport框架的虚拟磁盘驱动?

估计是闲的蛋疼吧。

又或者为了彰显曾经无法理解和实现的,现在具备了一定基础,再来实现,却发现变得异常的简单。

记得以前开发winxp下的Scsiport框架的虚拟磁盘驱动,非常的麻烦,

不但要解决虚拟模拟部分,安装驱动电脑还得重启好几次才能安装成功。

以前讲述过虚拟磁盘驱动,

有基于filedisk模型的,这个纯粹就是在文件系统下面挂一个接口,

直接接收来自文件系统转化而成的数据块读写指令, 不能被磁盘管理器识别,也不能被windows当成真正的“设备”,

但好处也显而易见,模型简单易懂,很快就能实现,

而且节省了windows下驱动命令的弯弯绕绕的浪费(这个纯粹基于心理上,现在的电脑性能根本不在乎这些)

有基于Storport和scsiport框架的,

而Storport 属于windows中目前几乎所有磁盘驱动(包括虚拟磁盘,更包括真实的磁盘)通用的框架。

因此如果需要开发不管是虚拟的,还是基于硬件的,掌握Storport框架,基本就可以了。

而我们今天要讲述的,则是从更底层的,更本质角度去实现虚拟磁盘驱动。

简单的说,使用原始代码,自己码代码来实现类似Storport框架一样的磁盘驱动,

因为Storport框架的代码我没看过,也不清楚Storport框架底层代码是如何实现的。

但是根据windows的wdm驱动的运行规律,PDO和FDO等基本常识

Storport实现的大致流程,跟我准备讲述的应该比较接近,或者总体思路比较接近。

开始之前,我们需要一些准备知识。

首先熟悉PDO和FDO等基本概念。

需要熟悉windows平台下通用的总线驱动开发流程,

熟悉SCSI磁盘相关命令。

这些准备知识,这里不再具体阐述。

而运气好稍好,以上我都具备,因此从底层码代码来实现虚拟磁盘驱动,就显得比较容易了。

首先,我们抛开了storport 框架,自己码代码来实现虚拟磁盘驱动,是需要建立在虚拟总线驱动之上的。

当初为了实现USB虚拟总线驱动,曾经非常认认真真的研究了微软的toaster例子,而且是研究wdm的,不是kmdf框架。

为何需要这么做,那是因为window7,8 这些的系统不像windows10,有专门提供UDE和UCX框架来实现usb控制器的虚拟。

winxp,win7,win8就只能从更底层的总线驱动来实现USB主机控制器的模拟。

而且USB相关的指令是异常的繁多。

当初为了开发usb总线驱动,还重新构建了基于wdm模型的总线驱动框架代码,其实主要是处理 IRP_MJ_PNP, IRP_MJ_POWER命令,

别看只有两个命令,其实处理的内容非常多,还得考虑如何管理PDO等内容。

自己实现的这个框架代码,目前用在了 USB虚拟主机控制器的开发上(以前的文章阐述过usb虚拟总线的开发,有兴趣可去查阅),

也用在了 Indirect Display Driver (win10的虚拟显示器驱动)的总线枚举上,

同时也用在了目前讨论的虚拟磁盘开发上,然后发现:虚拟总线驱动这玩意用处还挺多。

打开设备管理器,查看 “存储控制器” 和 “ 磁盘驱动器” 里边的内容,

会发现里边对应一个磁盘控制器驱动(存储控制器)和一个或者多个对应的磁盘(磁盘驱动器),

不同电脑,对应的磁盘控制器不一定在 “存储控制器” 里边,有可能是对应着“IDE ATA/ATAPI 控制器” 等,

反正不管怎么说,一块磁盘,都会对应着磁盘控制器驱动。

如果我们把这个控制器驱动称作 FDO ,把对应的每个磁盘称作 PDO,现在我们来具体分析FDO和PDO的对应关系。

为了更好理解,用下面的实物图来说明,

这个是我笔记本电脑上的磁盘情况,使用Devicetree工具软件分析磁盘驱动里边的设备情况:

在上图中,左边是“设备管理器”,中间是Devicetree显示的内容,右边是Apple磁盘显示的属性。

Apple 是笔记本电脑上的唯一块真实硬盘

设备管理器里 “Apple Solid State Drive Device“ 是Apple磁盘控制器驱动。

它的驱动名是 AppleSSD.sys,这可以从右边的属性里看到。驱动是基于Storport框架的(现在绝大部分磁盘驱动基本都是基于此框架),

在中间Devicetree软件里可以看到 \\Driver\\AppleSSD 驱动里创建了两个Device设备,我们重点关注 \\Device\\00000037 。

可以看到它上面挂载了\\Device\\Harddisk0\\DR0, 接着有挂载了 \\Driver\\partmgr,

很显然 \\Device\\00000037 是块磁盘设备, 是最底层的 PDO 设备。

同时从右边属性里可以看到,它是 “Apple APPLE SSD AP0512 SCSI Disk Device” ,

也就是设备管理器“磁盘驱动器”里边的 对应的磁盘。

这个关系也就挺明显了:

Apple Solid State Drive Device 磁盘控制器驱动(也就是FDO)里创建了某块磁盘的 PDO,并且管理着这个磁盘PDO的行为模式。

另外设备管理器还出现了

“Fanxiushu Primitive Virtual SCSI Adapter” 和 “Fanxiushu xFsRedir Virtual SCSI Adapter”

两个 虚拟磁盘控制器,

其中xFsRedir对应为xFsRedir软件开发的扩展虚拟磁盘驱动,是基于Storport框架的。

而Primitive 则是本文讨论的,直接基于底层的虚拟总线驱动实现的虚拟磁盘控制器。

他们都能同时管理多块磁盘设备。

理清了上面的关系,我们会发现,基于原始虚拟总线驱动开发虚拟磁盘的总体框架,变得异常的简单

(当然得先熟悉通用的虚拟总线驱动和FDO,PDO这些基本的东西),

我们甚至可以首先开发一个通用的虚拟总线驱动程序,驱动里边创建一个或多个PDO设备,

PDO怎么创建则取决于你以后打算如何规划这个磁盘设备,现在就假设简单的创建一个PDO设备。

现在的问题是,如何把这个通用的虚拟总线驱动变成一个虚拟磁盘驱动,

让windows系统认为我们开发的是一块磁盘驱动程序。

首先,在制作 inf安装配置文件的时候,把[Version]里的 CLASS设置成 SCSIAdapter类,

ClassGUID 设置成 4D36E97B-E325-11CE-BFC1-08002BE10318。

在这个SCSIAdapter类里面,windows会把驱动当成是磁盘控制器,自然就会发送一些磁盘相关的命令到我们的驱动。

于是接下来的重头戏,就是得正确响应这些跟磁盘相关的命令。

(当然我们在调用IoCreateDEvice创建设备的时候,设置 FILE_DEVICE_DISK,表示我们创建的设备类型是 磁盘类型。)

而接受磁盘相关命令的主要是PDO设备。

以下是一些伪代码来描述这样一个总体框架。

NTSTATUS DriverEntry(.....)

........

DriverObject->DriverExtension->AddDevice = AddDevice;

DriverObject->DriverUnload = DriverUnload;

DriverObject->MajorFunction[IRP_MJ_PNP] = pnp_dispatch;

DriverObject->MajorFunction[IRP_MJ_POWER] = power_dispatch;

DriverObject->MajorFunction[IRP_MJ_CREATE] =

DriverObject->MajorFunction[IRP_MJ_CLEANUP] =

DriverObject->MajorFunction[IRP_MJ_CLOSE] =

DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =

DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vscsi_dispatch;

..........

static NTSTATUS vscsi_dispatch(PDEVICE_OBJECT deviceObject, PIRP Irp)

NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;

PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

base_exts_t* base = (base_exts_t*)deviceObject->DeviceExtension;

if (base->devType == 1) // FDO

fdo_exts_t* fdo = (fdo_exts_t*)base;

/

switch (irpStack->MajorFunction)

case IRP_MJ_CREATE:

case IRP_MJ_CLEANUP:

case IRP_MJ_CLOSE:

case IRP_MJ_DEVICE_CONTROL:

return ioctl_dispatch_function(deviceObject, Irp);

case IRP_MJ_SYSTEM_CONTROL:

return wmi_system_control(deviceObject, Irp); ///

default:

status = STATUS_INVALID_DEVICE_REQUEST;

DPT("V: --FDO: Not Process MJ=[0x%X]\\n", irpStack->MajorFunction);

break;

//

else // PDO

/

switch (irpStack->MajorFunction)

case IRP_MJ_CREATE:

case IRP_MJ_CLEANUP:

case IRP_MJ_CLOSE:

DPT("V: ++PDO: Do Raw Create Close. MJ=0x%X\\n", irpStack->MajorFunction ); 2016-06-11

status = STATUS_SUCCESS;

break;

case IRP_MJ_DEVICE_CONTROL:

return pdo_devicecontrol_dispatch(deviceObject, Irp, irpStack, (pdo_exts_t*)base);

case IRP_MJ_INTERNAL_DEVICE_CONTROL:

return pdo_srb_trans_dispatch(deviceObject, Irp, irpStack, (pdo_exts_t*)base);

default:

status = STATUS_INVALID_DEVICE_REQUEST;

DPT("V: ++PDO: Not Process MJ=[0x%X]\\n", irpStack->MajorFunction);

break;

/

/

return complete_irp(Irp, status, 0);

以上 vscsi_dispatch 派遣函数中,

其中 ioctl_dispatch_function 函数处理的FDO相关IOCTL,这个IOCTL可以传递我们自己定义的一些数据,

比如我们打算把虚拟磁盘的读写数据传输到应用层来处理,以及其他一些自定义命令。

总之这个函数里不传输windows系统命令。

而 pdo_devicecontrol_dispatch 和 pdo_srb_trans_dispatch 函数则是必须响应windows系统下发的IOCTL命令。

其中 pdo_devicecontrol_dispatch 响应windows系统定义的命令,目前通过测试 其中

IOCTL_STORAGE_QUERY_PROPERTY 命令是必须正确响应并处理的,否则系统不会认为这是磁盘驱动。

当然还包括其他一些命令,具体可以在不同系统下测试,然后再做处理。

pdo_srb_trans_dispatch 函数处理的就是核心的磁盘通信了,基本都是SCSI命令。

这个的处理基本和 Storport框架里边关于 SRB (SCSI_REQUEST_BLOCK 结构)的处理基本一致。

srb通过IRP获取,如下:

PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;

PCDB cdb = (PCDB)srb->Cdb;

因此如果你熟悉 Storport框架,以及scsi命令的话,这个也不是难事。

因为在以前阐述虚拟磁盘驱动的时候,阐述过相关内容,

这里也就不再赘述了。

最近计划着重温多年前的关于无盘启动的技术,其实当年(大概是10年前,因为根据cdsn博客上写的文章的时间推断)

实现了winxp的无盘启动,到后来win7没成功,什么原因后来一直处于搁置,这次有机会看看能不能解决。

当然现在关于无盘启动的现成软件也挺多,

而且现在也并不如当初那么火热,毕竟技术在发展,现在使用云桌面构建通用的远程平台应该更合适。

无论如何,如果有空就折腾来玩玩。而且打算这次把虚拟磁盘换成目前新开发的这种原始总线驱动方式来进行无盘启动。

关于xFsRedir软件,也计划把Storport框架的扩展虚拟磁盘驱动换成这种原始总线驱动方式。

以上是关于Windows 虚拟磁盘驱动开发(采用原始办法实现类似Storport框架的相同功能)的主要内容,如果未能解决你的问题,请参考以下文章

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

Openstack中给windows虚拟机加载virtion驱动

如何在 Windows 上创建虚拟网络接口?

PVS架构之VHD虚拟磁盘

Windows中为啥采用树形目录结构管理文件?