文件过滤驱动学习笔记(二)
1. 概述
刚学习文件系统过滤时只是做一些简单的应用也没有深究其中的细节;说起来对文件系统过滤也只是一知半解惭愧的很。后与人讨论发现很多细节自己很模糊;比如其中涉及的驱动对象之间的区别、卷设备加载对文件过滤驱动的影响等。自己还是需要仔细研究一下,最近查了一些官方及前辈们的资料似乎有些理解在此记录下来做个笔记。
2. 相关对象说明
在文件过滤驱动学习中会遇到几种设备对象总是混淆。最近硬着头皮查阅DDK才有些理解。
2.1. 存储设备(Storage Device)
存储设备可以理解为一个磁盘、一个CD;它可以物理的也可以是逻辑的,它上边可以有一个或多个卷设备对象。大多数存储设备是一个PnP设备,它们由PnP管理器加载。存储设备表现为PnP设备树(PnP Device Tree)上的一个节。注意:文件系统驱动和文件系统过滤驱动都不是PnP设备驱动。
2.2. 存储卷(Storage Volume)
存储卷是一个存储设备如固定磁盘,软盘,CD盘,格式化后存储的目录与文件。一个很的大卷可以被分成一个或多个逻辑卷;每一个卷都会被格式化成指定的一种格式,如NTFS,FAT等。它通常是一个物理设备对像(PDO)。一句话就是我们常看到所谓的分区。
2.3. 文件系统卷设备对象(File System VDO)
一个存储卷被文件系统加载时就会产生一个文件系统卷设备对像(File System VDO);它总是与指定的逻辑或物理设备相联结。DDK上说文件系统卷设备对象是不需要命名的,因为给它命名会带来安全隐患。其实它就是文件系统控制对像生成的用于完成文件读写等操作的设备对象。它与存储卷对象是一一对应用的。文件系统过滤驱动就是要附加到这种设备对象上过滤读写相关的操作。
2.4. 文件系统控制设备对象(File System CDO )
文件系统控制设备对象是一个文件系统的入口,它是存储在全局文件系统队列里的。一个文件系统驱动在入口创建一个或多个CDO,例如FastFat就创建两个CDO。一个是针对固定媒体一个针对可移动媒体。CDFS只创建一个因为它只有一个移动媒体。它是文件系统驱动接收操作系统控制命令的设备对象,例如当有一个存储卷对象产了,没有挂载相应的文件系统那么操作系统会给相应的文件系统驱动发送一个挂载命令,让它生成一个文件系统卷设备对象来挂载到这个卷上。这样我们就能看到这个卷上的文件了。所以文件系统控制设备对象是文件系统过滤驱动要过滤的另一个重要对象,因为在卷加载时会发一个消息给文件系统驱动,需要知道有新的卷加载安装就需要对这个设备对象进行过滤。
3. 加载过程
3.1. OS启动时的加载
3.1.1. OS的加载过程中的文件系统
1) 在系统的启动(BOOT)期间,操作系统加载boot file system, RAW file system和所有启动类型为SERVICE_BOOT_START的驱动,完成之后把控制权交给系统内核。当内核得到控制权时这些驱动已经加载到内存了。驱动的加载顺序是由驱动加载顺序组来分配决定的;在文件系统过滤驱动中,那里新加入文件系统过滤驱动加载组之中的驱动在其它所有过滤驱动之前加载。
2) I/O管理器创建一个四个元素文件系统全局队列,分别是CD-ROM,磁盘,磁带还有网络文件系统。之会每当新注册一个文件系统,它的控制设备对象就会添加到队列中。在这时因为没有文件系统注册,所有这个队列是空的。
3) PnP管理器调用RAW file system和所有启动类型为SERVICE_BOOT_START驱动的DriverEntry例程。如果类型为SERVICE_BOOT_START驱动有依赖的驱动需要启动也这在这时启动。PnP管理器调用Boot Device Driver的AddDevice例程来启动Boot Device。如果Boot Device有子设备(child device),它们也将被列举出来;如果它们的驱动是Boot-Start Driver(启动类型为SERVICE_BOOT_START的驱动)那么这些子设备也将被配置和启动。如果一个设备的驱动不完全是Boot-Start Driver它们将不被启动,PnP管理器会为它们创建一个DevNode(设备节)。这个点上,所有Boot Driver和Boot Device都被加载和启动。
4) PnP管理器遍历PnP Device Tree,定位并加载那个与每个devnode关联但没有运行的设备。当每一个PnP设备启动的时候,如果他们有子设备则会遍历他们。PnP管理器配置这些子设备并加载他们的驱动启动设备。PnP管理器加载每一个设备驱动时并不考虑他们的StartType、LoadOrderGroup、Dependencies属性。这么一步PnP管理器只配置和启动那些可以枚举到的设备和它们的子设备。不启动那个不可枚举到的设备,即便能枚举到他们的子设备。
5) PnP管理器初始化那个启动类型为SERVICE_SYSTEM_START的驱动。注意,文件系统识别器(file system recognizer FsRec)就在这个时候被加载。别外需要注意是,它虽然在“Boot File System”加载顺序组中但它并不是一个Boot File System。实际的Boot File System是在BOOT 卷(Boot Volume)被安装(mounted)时被Boot Process加载的。在后来的SERVICE_SYSTEM_START阶段,文件系统在文件系统加载顺序组被加载,它包括命名管道文件系统、邮槽文件系统。但它不包括那些基于媒体的文件系统如NTFS,FAT,UDFS,CDFS等等。网络文件系统及在网络加载顺序组的也在这个时候加载。
6) 所有驱动在boot time 被初始化后,I/O管理调用那些驱动的重新初始化例程。
7) 服务控制管理器加载所有SERVICE_AUTO_START的服务
3.2. 卷挂载的过程
3.2.1. 文件系统识别器
在系统启动后,存储设备上的所有卷都加载到了系统,然而并不是所有内建(build-in)加载且也不是所有文件系统卷都会被安装(mount)。文件系统识别器的功能需要处进IRP_MJ_CREATE请求,查找访问卷的文件系统是否被安装没有就安装它们。在ReactOS-0.3.4中可以找到文件系统识别器的相关实现。XPSP2中FS_REC中FsRecCreateAndRegisterDO是用来注册某一种文件系统的识别器对像的。可以断下来观察一下。其中和ReactOS-0.3.4略有差别大体相同。
3.2.2. 卷挂载
当我动态插入一个磁盘时,PnP管理器会为我们生成一个存储设备。并发现请求向Mount Manager发出请求,Mount Manager收到请求后收集这个存储设备的信息并为他们生成存储卷名。这时一个磁盘的插入动作完成。
如果我们要对这个磁盘发生读写操作,系统会解析这个路径如J:/,如果相应的卷没有安装文件系统,就给文件系统识别器发一个IRP_FILE_SYSTEM_CONTROL的请求。文件系统识别器会识别出是什么文件系统并加载它,同时这个请求再会发给真实的文件系统来完成并安装文件系统卷。这样卷的挂载就完成了。
这也就是我们为什么要注册一个回调去监视文件系统控制对像的激活并绑定它。同时也去过滤每一个文件系统控制设备对象的IRP_FILE_SYSTEM_CONTROL请求。