前段时间完成了,基于软件驱动实现磁盘还原的保护程序,一直没有时间总结一下这方面相关的知识,
方便以后的查阅。
明天,课设验收,乘着今晚,总结一下在开发过程中的相关问题。。。
首先,我们应该知道磁盘设备驱动和文件系统驱动的区别,它们是两种不同的驱动。前者是一种存储设备驱动,
后者管理数据在存储设备上的存储格式的驱动,它们的分工不同。磁盘驱动更底层,文件系统驱动在它的上部。
这点务必要了解到。
在存储设备驱动中,实际与硬件设备打交道的是微端口驱动,在它上部的就是类驱动,这里说的磁盘驱动就是
一种类驱动,类驱动具体的功能实现是通过微端口驱动去实现的,它就是抽象出来,供上层的文件系统驱动调用(这里体现出了一种经典的设计方法)。这样,文件系统就不用直接与硬件打交道了。在文件系统看来,所有的磁盘就是一个磁盘设备。
一个磁盘设备对象对应一个物理的磁盘,卷是文件系统找到磁盘设备后挂载在磁盘设备上生成的一个新的设备。这种新的设备
能够进行很多的操作,
比如 1:生成文件
2:删除文件
3:修改文件
4.。。。
但是,磁盘设备对象则没有这么多的功能,它不知道上边还有文件系统,它只能读和写,以及一些获取一下磁盘的信息。
在这里,我们可以通过两种方法来创建磁盘设备对象
1:IoCreateDisk ,这个是ifs 中XP 下的例子使用的方法
2:IoCreateDevice ,这个是FileDisk 使用的方法
后者比较复杂,可能是为了要兼容 WIN 2000,前者参数只有两个,具体可以参看 DDK
下边说一下分发函数,在磁盘设备驱动中,我们只要处理五个分发函数即可:
DriverObject->MajorFunction[IRP_MJ_CREATE] = FileDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FileDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] =FileDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = FileDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =FileDiskDeviceControl;
// 卸载例程。
DriverObject->DriverUnload = FileDiskUnload;
在描述接各个函数之前,看一下定义好的结构:
typedef struct _DEVICE_EXTENSION
{
BOOLEAN media_in_device;
HANDLE file_handle;
FILE_STANDARD_INFORMATION file_information;
BOOLEAN read_only;
PSECURITY_CLIENT_CONTEXT security_client_context;
LIST_ENTRY list_head;
KSPIN_LOCK list_lock;
KEVENT request_event;
PVOID thread_pointer;
BOOLEAN terminate_thread;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
media_in_device:
指这个设备是否已经指定了一个文件作为存储媒质。
这是一个用文件来虚拟磁盘的驱动。
那么一个磁盘应该对应一个实际存在的文件。
读写这个磁盘的请求最终转变为对文件的读写。
如果一个磁盘设备对象还没有指定文件,那么这个内容是FALSE.
file_handle:
文件句柄。也就是这个虚拟磁盘所对应的文件。
file_information:
这个文件的一些信息。
read_only:
是否只读。
security_client_context:
访问文件的时候需要使用的一个线程客户安全性上下文。
list_head:
是一个链表头。一部分irp(windows发来的请求包)被放入这个链表中。
我们为每个磁盘对象开启一个系统线程(处理线程),专门用来处理这些请求。
list_lock:
是为了保证链表读写同步的锁。
request_event:
是一个事件。当链表中没有请求的时候,处理请求的系统线程并不做任何事情,而只等待这个事件。
当有请求到来,我们把请求放入链表,然后设置这个事件。处理线程就会开始处理这些请求。
thread_pointer:
是线程的指针,用来最后等待这个线程的结束。
terminate_thread:
是一个标志。如果设置为TRUE,处理线程执行的时候检测到这个,就会把自己终止掉。
现在来描述一下函数:
1:FileDiskCreateClose,FileDiskUnload 这两个我们可以不用怎么样关心,因为实际用处不大
2:FileDiskReadWrite:处理读写请求的方式,这里,我采用了一个线程独立的处理读和写,方便
实现序列化的来处理所有的请求(当然,直接在这个函数体中来处理也是可以的,不过这个函数比较容易造成重入)。
具体是这样的,我首先在磁盘驱动加载的时候,或者叫初始化的时候,创建了一个线程来负责处理读和写,这个线程
不会退出,除非重启或关闭电脑。然后,我通过事件来通知线程处理队列中的请求。
在这个线程中,我一直等待着被唤醒。加入被唤醒,我就从队列头取出请求,执行操作,也就是读写重定向,达到欺骗
操作系统的目的,当然,在初始化的阶段,我已经完成了很多准备工作,以后再详细说。
3:FileDiskDeviceControl
这个是负责处理一些设备控制请求的,返回以下磁盘相关的信息,当然也可以作为与应用程序通讯的窗口,这些功能号,
可以直接去查看DDK ,这里不再赘述。
现在,说一下上边的那个线程到底做了什么吧。可能会比较多,
因为核心就是这个线程了,但是我会简单的描述一下过程:
1:当有线程的读写来的时候,我会首先判断,是读还是写,
如果是读 ,判断它的读的范围,是否在经过我们重定向后的地址上边,如果是,则从这部分开始读取;
反之,则是写,写的时候,我们的考虑几个问题,
首先,有三个文件我们必须放过:
1:休眠文件;
2:bootstat.dat(记录系统是否启动失败)
3:pagefile.sys(分页文件)
这三个文件,我加入了一个表中,这个表记录了这三个文件在磁盘的位置,如果是对这些文件的读写,我们都放过
然后,如果写入的是其他的地址(磁盘上的),PS:初始化的时候,我们已经为磁盘创建好了一张位图,这个位图记录
了每个磁盘的存储信息。
现在,我们开始使用它们了;
我们写判断写入的位置是否是之前在我们已经记录在案的地址,也就是说这部分存储了内容,不允许其他数据占据了,
但是,我们不能阻止它写到磁盘啊,怎么办呢?
这里,我想到了,对这个IO操作进行重定向,也就是改变它写入的地址并且记录下我们写入的地址即可(加入到一个定义好的数据结构中),以后处理的时候,我们就现在这个表中查找,读出来就行了。。。
现在,我们的程序基本就完成了,剩下的就是处理各种极端的情况,提高程序的容错处理和稳定性了。。。
细心的人可能发现,既然写入了磁盘,如果磁盘写满了怎么办,这里其实很好做,我们身处底层,简单的为
自己留出足够的空间就可以了,这很容易办到的。。。
就写这么多了,明天验收完,有时间在整理文档吧。。。