支持存储类驱动程序中的装载管理器请求
系统提供的装载管理器负责管理卷名称。 对于每个卷,它存储一个唯一且永久标识与卷的名称,即使在从系统中删除卷之后也是如此。 它还管理较少的永久名称(如驱动器号),这些名称在重新启动时会保留,但分配可能会随着卷添加到系统中或删除而更改。
装载管理器通过创建卷的设备对象的符号链接,为系统中的每个卷提供唯一接口。 由于符号链接本身及其目标设备对象在系统重新启动时不会保留,因此装载管理器会在注册表中的持久名称数据库中保留符号链接的名称。
此符号链接名称称为唯一 卷名称。 与传统卷标签一样,当系统重启时,它仍会保留。 与驱动器号和卷标签不同,此名称是唯一的。 唯一卷名称的格式如下,其中 GUID 是标识卷的全局唯一标识符。
“\??\Volume{GUID}\
装载管理器的持久名称数据库位于注册表的 SYSTEM hive(HKLM/SYSTEM/MountedDevices)的 MountedDevices 注册表项中。 除了唯一的卷名称外,装载管理器还会在其持久名称数据库中存储 装入点 名称。 装入点名称可以进一步细分为两个类别:Win32 样式的路径名称,用作已装载卷文件系统的根目录,以及驱动器号。
数据库中的每个永久性符号链接名称都显示为 MountedDevices 键下注册表值的名称,并附带唯一 ID。 唯一 ID 是卷的另一个唯一标识符(不同于唯一卷名称)。 它有助于确定哪些潜在的大量持久符号链接名称引用同一卷。
例如,具有唯一卷名称为 **“\\?\Volume{**7603f260-142a-11d4-ac67-806d6172696f }的单个卷 \” 可能有随附的驱动器号“\DosDevices\D:”和两个装入点“\DosDevices\C:\mymount”和“\DosDevices\E:\FilesysD\mnt”。 此组合将生成装载管理器持久符号链接名称数据库中的四个条目:一个用于唯一卷名称,一个用于驱动器号,两个用于两个装入点名称。 这四个条目将共享相同的唯一 ID。 因此,查看 MountedDevices 注册表项的人能够检测到所有四个永久性名称都指向同一卷。
以下屏幕截图演示了 MountedDevices 注册表项中持久名称的显示方式。
装载管理器依赖于即插即用设备接口通知机制来提醒其卷到达和删除。 因此,每个客户端(即每个卷驱动程序(通常是类驱动程序)都必须通过调用 IoRegisterDeviceInterface 在MOUNTDEV_MOUNTED_DEVICE_GUID接口类中创建接口,以通知装载管理器到达所管理的卷。 MOUNTDEV_MOUNTED_DEVICE_GUID接口类 GUID 在 mountmgr.h 中定义。
收到卷接口到达的即插即用通知后,装载管理器会发送客户端三个设备控制 IRP:
- IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
- IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
- IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
为了响应这三个 IOCTL,客户端应分别返回位于系统对象树的设备目录中的卷的非永久性设备对象名称(或目标名称),例如:“\Device\HarddiskVolume1”),唯一的卷 ID 和建议的永久性符号链接名称。 尽管客户端可以选择忽略IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,但在收到IOCTL_MOUNTDEV_QUERY_DEVICE_NAME或IOCTL_MOUNTDEV_QUERY_UNIQUE_ID时,需要提供唯一的卷 ID。 装载管理器完全依赖于客户端来提供唯一的卷 ID。 如果客户端未提供它,则装载管理器无法将装入点(如驱动器号)分配给卷。
如果客户端通知装载管理器其卷的到达,但在查询时无法为卷提供唯一 ID,卷将放置在已装载的死机设备 列表中。 出现这种情况时,客户端可以将IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES IOCTL 发送给装载管理器,请求装载管理器重新扫描其已装载的设备列表,并再次尝试在列表中查询其各自卷的唯一 ID 的客户端。
装载管理器收到新引入的卷的唯一卷 ID 后:
- 它会在其数据库中搜索分配给该唯一 ID 的所有永久性名称。
- 它为每个永久性符号链接名称创建指向卷的符号链接。
当装载管理器检测到卷已关闭时,它会删除指向设备对象的符号链接,而无需删除装载管理器数据库中的相应符号链接名称。
有关装载管理器客户端如何创建永久性符号名称的信息,请参阅 IOCTL_MOUNTMGR_CREATE_POINT。
装载管理器客户端发送的 I/O 控制代码
装载管理器发布一个接口,该接口允许装载管理器的客户端为卷设置、查询和删除永久性名称。 若要访问此接口,客户端可以使用 Mountmgr.h 中定义的对象名称MOUNTMGR_DEVICE_NAME获取指向装载管理器设备对象的指针。 例如:
// Obtain a pointer to the mount manager device object &
// use it to send any of the I/O Control codes in this
// section to the mount manager.
RtlInitUnicodeString(&name, MOUNTMGR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(&name,
FILE_READ_ATTRIBUTES,
&fileObject, &deviceObject);
irp = IoBuildDeviceIoControlRequest(
IOCTL_MOUNTMGR_CREATE_POINT,
deviceObject, createPoint, createPointSize,
NULL, 0, FALSE, &event, &ioStatus);
status = IoCallDriver(deviceObject, irp);
为了简洁起见,简化了此伪代码示例中的调用序列。 有关更完整的伪代码示例,请参阅 IOCTL_MOUNTMGR_CREATE_POINT。
装载管理器客户端可以将任何记录的IOCTL_MOUNTMGR_XXX 控制代码发送到装载管理器,例如IOCTL_MOUNTMGR_CREATE_POINT。
装载管理器发送的 I/O 控制代码
装载管理器可以将任何记录的IOCTL_MOUNTDEV_XXX 控制代码发送到其客户端,例如IOCTL_MOUNTDEV_QUERY_DEVICE_NAME。