文件过滤驱动实现目录重定向(一)

谈论这个问题前,先看看一个情况:

比如你上班的公司,可能有多个文件服务器,这些文件服务器通过FTP或者远程共享目录方式提供目录共享。
而你可能会经常性的从一个文件服务器切换到另一个文件服务器上去找资料。
而且如果是FTP的话,还得准备一个FTP客户端。
这么折腾也许觉得有点麻烦,也许就会想:有没有办法让所有这些文件服务器目录共享到我本地一个目录下边,
等我需要的时候,我直接打开这个本地目录,然后操作这些文件就跟完全操作我本地机器上的文件一样方便。

其实早在 win2003 server的系统中,就提供了类似的叫DFS的分布式文件系统的功能。
可惜只在windows服务器版本才有这个功能。

因此为了在所有windows版本中实现此功能,或者是为了更深入的理解文件系统,
有必要掌握如何利用文件过滤驱动来达到目录重定向的功能。
这里讨论的目录重定向,不是简单的在文件过滤驱动里的IRP_MJ_CREATE里修改文件路径到新位置,
然后返回 STATUS_REPARSE来重定向;
也不是在应用层用DeviceIoControl执行FSCTL_SET_REPARSE_POINT 来映射目录,
也不是调用subst命令把某个本地目录映射成新盘符;
而是拦截有关 文件操作的所有 IRP,然后把文件请求的所有数据转向到别的地方处理。
大致过程:
首先定义一个需要重定向的目录,比如 D:\Redir, 创建一个文件过滤驱动挂载到 D: 盘。
当IRP_MJ_CREATE进入到过滤驱动,通过分析这个请求的文件名,判断是不是在我们监控的目录里,
如果是,则把 这个IRP的文件对象,就是FILE_OBJECT保存起来(一般通过MAP结构进行关联保存),
并把这个创建的IRP重定向到真正的文件打开操作,
以后除开 IRP_MJ_CRAETE的所有IRP,通过判断是否在关联结构里找到这个FILE_OBJECT, 
来确定是否是属于这个监控目录的文件请求,如果是则把IRP转向处理,
在IRP_MJ_CLOSE中,删除在关联结构中保存的FILE_OBJECT对象。
原理看起来并不复杂,可是真正要处理这些转向的IRP的细节,可就不那么轻松了。

文件过滤驱动拦截的IRP主要包括以下几个:
IRP_MJ_CREATE,文件创建操作,文件的任何操作,都是从这里开始的。
IRP_MJ_CLEANUP,文件的HANDLE句柄全部关闭会触发这个消息
IRP_MJ_CLOSE,文件对象 FILE_OBJECT引用减为0,文件对象即将被删除时触发。
IRP_MJ_READ、IRP_MJ_WRITE, 文件的读写操作
IRP_MJ_QUERY_INFORMATION 查询文件信息,比如文件创建修改时间,文件大小等等。
IRP_MJ_SET_INFORMATION 设置文件信息,比如删除文件,重名文件,更改文件大小等等。
IRP_MJ_DIRECTORY_CONTROL 查询某个目录下的文件和子目录信息。

还有几个也需要处理的,如果不处理可能某些文件操作不正常。
IRP_MJ_QUERY_VOLUME_INFORMATION ,查询目录所在的卷设备信息。
IRP_MJ_QUERY_SECURITY 查询安全信息,在win7系统得处理这个,否则exe程序无法执行。
(还包括其他需要处理的IRP)。

看起来处理的IRP不算多,我当初也是这么想的,可是处理的细节却挺多。
(查看 WDK的实例里边的 fastfat目录 ,微软实现FAT32文件系统的源代码,那里边除了代码还是代码,而且还是异常的多)

我在前几篇文章中讲过虚拟磁盘驱动, 虚拟磁盘驱动加载的时候,会在Windows磁盘管理器出现一个新的磁盘,把这个磁盘分区格式化之后,可以正常的把他当成一个真正的磁盘来操作,可以在上边存放,删除文件目录等。
这些操作,经过Windows系统重重解析处理,最后会转换成对虚拟磁盘驱动单纯的对磁盘扇区的偏移读写操作,
然后把对扇区的偏移读写,通过多种途径比如是一个真正的文件,保存起来。操作这个虚拟磁盘,最终就变成操作一个单纯文件。
类似的概念,比如 Windows自带的vhd或vhdx格式的虚拟磁盘文件。
虚拟磁盘驱动,应该说可以很方便的把服务器端的一个单纯的文件,映射到客户端形成一个或者多个磁盘。
但是如果有这样的需求,假设服务端保存的不是一个单纯的文件,而是一个或者多个目录,
你需要把这些目录映射到客户端的一个新的盘符。或者映射到某个现有磁盘的某个目录下边。
(我觉得映射到现有的一个目录更酷,就跟Linux系统那样就一个根目录,下边可以挂载多个不同的文件系统)
这种要求,虚拟磁盘驱动就无法解决问题了。
映射到新的盘符,当然可以通过windows自带的映射网络驱动器的功能,把服务端目录映射到客户端的一个新的盘符中。
但是如果我们想从原理上来理解windows究竟做了些什么,以及我们自己想更加灵活和随心所欲的控制映射行为,
那解决办法就是上文提到的利用文件过滤驱动来拦截具体的IRP进行处理了。
如果你只是映射远程目录到一个新盘符,你可以不使用文件过滤驱动,
而是自己开发一个自定义的文件系统,再开发一个分区驱动,然后把分区格式化成自己的文件系统,
这样也能达到把远程目录映射到本地新盘符的目的。详细办法后面章节会陆续提到。

先简单说说Windows文件系统的工作流程。
当某个新的磁盘被加到硬件上,Windows系统检测到有新的磁盘硬件被添加,当然首先 就是加载这个磁盘对应的驱动,
然后就会在磁盘管理器里边识别出这个磁盘设备,最后经过我们对这个磁盘的一系列的磁盘初始化操作之后,
他会把已经安装到windows中的文件系统(NTFS,FAT32,CDFS,RAWFS,当然可能还包括我们自己开发的文件系统),
并且是你在分区格式化时候指定的文件系统(现在一般常用的是NTFS驱动)Attach到这个新磁盘的驱动之上,
我们在应用层调用WIN32 API函数,CreateFile, ReadFile,WriteFile等文件操作函数来读写这个新磁盘的数据。
调用CreateFile,进入ntdll的NtCreateFile,然后进入到内核的NtCreateFile函数,内核的NtCreateFile里边,
创建一个 IRP_MJ_CREATE 的IRP,并填写相关参数,
然后调用NTFS系统的顶层驱动(顶层驱动下边可能有一个或多个文件过滤驱动),
如果过滤驱动没有拦截,NTFS文件系统驱动最终解析这个请求,
并转换成对这个磁盘驱动的 对扇区的读写请求,然后调用下层的磁盘驱动进行磁盘扇区的读写操作。
(如果磁盘驱动是虚拟磁盘驱动,这就又回到刚才上边提到的虚拟磁盘驱动的内容了)。
完成磁盘读写之后,然后一层层的返回,最终回到应用层的CreateFile。
ReadFile和WriteFile等WIN32 API函数基本也是这样的流程(当然还包括FastIO和Cache IO过程)。

显然的,如果我们在文件过滤驱动中拦截了某个目录下的所有文件操作的IRP,
那么对这个目录下的文件操作的行为就不再下发到底层的NTFS文件系统,自然也不会进入到真正的磁盘IO操作。
如果我们再把拦截到的IRP重新解析处理,让他返回我们自己的数据,这样就达到了这个目录重定向的目的。

要实现目录重定向,我们需要熟悉文件过滤驱动的框架。
可以借鉴sfilter源代码,也许sfilter代码挺多,研究起来挺烦。
其实很简单的一句话,sfilter就是标准的 NT式过滤驱动,外加一些如何处理FASTIO和动态挂载文件系统的代码。
FASTIO本身的函数也多,所以懒惰的时候,直接返回 FALSE即可,反正对系统影响不算太大。

如果你对文件动态挂载的代码不感兴趣,那你完全可以用简单的方式快速的实现一个文件过滤驱动框架。
(假如你给固定的 D: 盘挂载文件过滤驱动)
伪代码如下:

///首先打开 D:\根目录,获得 hFile 句柄.
OBJECT_ATTRIBUTES oa;
UNICODE_STRING us; RtlInitUnicodeString(&us, L"\\??\\D:\\");
InitializeObjectAttributes(&oa, &us, ....);
HANDLE hFile = ZwCreateFile( &oa ,....);

从句柄获得 FILE_OBJECT 对象
PFILE_OBJECT fileObj;
ObReferenceObjectByHandle(hFile, FILE_READ_ATTRIBUTES, // FILE_READ_DATA
  NULL, KernelMode, (PVOID*)&fileObj, NULL);
从FILE_OBJECT获得文件系统设备对象, 这个设备就是我们即将Attach的设备.
fileSystemDeviceObject = IoGetRelatedDeviceObject(fileObj);

myFilterDeviceObject =IoCreateDevice(...);
.....别忘需要继承源设备的设备属性

IoAttachDeviceToDeviceStackSafe(fileSystemDeviceObject,。。。)
就这么简单,一个过滤设备就创建好了。

当然别忘在DriverEntry中把所有派遣函数替换成我们的派遣函数
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i){
  ///
  DriverObject->MajorFunction[i] = sfPassThrough; //派遣函数
  /
 }

还必须记得填写 FAST_IO_DISPATCH 。否则会造成windows罢工蓝屏。
PFAST_IO_DISPATCH fastIoDispatch = (PFAST_IO_DISPATCH)ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_IO_DISPATCH), 'FXSD');
。。。。填写许多的FASTIO的回调函数

DriverObject->FastIoDispatch = fastIoDispatch; //

还必须知道一点就是,由于windows历史原因,PFAST_IO_DISPATCH中有6个函数:
AcquireFileForNtCreateSection
ReleaseFileForNtCreateSection
AcquireForModWrite
ReleaseForModWrite
AcquireForCcFlush
ReleaseForCcFlush
不提供支持,也就是这六个函数即使提供有效的函数地址,也不会被调用。
在我们的目录重定向驱动中,这是一个糟糕的事情,为什么呢?
因为我们必须保证被重定向的目录内所有的文件操作请求,不能下传给下层驱动,
否则很可能被下层的驱动识别成无效的请求,甚至蓝屏死机。
幸好在WINXP以上的系统中,可以调用 FsRtlRegisterFileSystemFilterCallbacks 来解决这六个函数问题
而且还可以在他注册的回调函数中,
直接返回 STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY,表示我们已经处理了这个请求,让他别再下传给下层驱动。
至于 STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY 这个状态码,没有文档说明达到这种效果,
但是 测试之后(包括最新的win8系统)确实是这样的。

如果你还想加入动态挂载文件系统的功能:
首先在DriverEntry调用 IoRegisterFsRegistrationChange 注册一个回调函数fsNotification,
fsNotification 函数原型如下,
VOID fsNotification(PDEVICE_OBJECT filesystemCtrlDevice, BOOLEAN FsActive);
filesystemCtrlDevice 就是对应的文件系统控制设备(比如NTFS文件系统),FsActive为TRUE表示加载,否则是卸载。
当有新的文件系统被加载进来或卸载,这个函数就会被调用。
加载新文件系统的时候我们创建一个过滤设备,挂载到文件系统控制设备上,
通过抓获这个过滤设备的 IRP_MJ_SYSTEM_CONTROL就能知道某个卷设备(就是我们看到的一个个盘符)何时被加载,何时被卸载。
然后我们再创建过滤设备挂载到这个卷设备上,这就等于是文章上边讲到的如何挂载到一个固定盘符比如D:盘的功能了。

如果卷设备在我们过滤驱动之前被加载,那么即使我们挂载过滤设备到文件系统控制设备上,也不会得到卷设备挂载的通知。
如何解决这个问题呢? 
这就需要在 fsNotification 回调函数中,在创建过滤设备Attach到文件系统控制设备之后,枚举所有已经被挂载的卷设备。
枚举伪代码如下:

IoEnumerateDeviceObjectList(filesystemCtrlDevice ->DriverObject, ....); 
//从文件系统控制设备所在的驱动对象,获取这个驱动下的所有设备

for(IoEnumerateDeviceObjectList获得的每个设备对象){
      首先判断设备类型对不对,
      接着调用 IoGetDeviceAttachmentBaseRef 获得设备栈最底层的文件系统设备,然后获取底层设备的名字,
            如果有名字说明是控制设备,而不是卷设备,只有没有名字的才算卷设备。
     最后 调用 IoGetDiskDeviceObject 函数,查看这个文件卷设备是否挂载到真正的存储卷设备上。
           如果是则说明这是个有效的卷设备,然后就是如上所述的创建过滤设备挂载到这个卷设备上。
}


就这样,一个文件过滤驱动的框架就做好了。,
(这里略过FASTIO的内容,因为他的函数太多,也费事,有兴趣可查看 微软的FastFat代码关于FastIO调用的例子。)

而重点就是如何处理 sfPassThrough 派遣函数,让他达到我们的重定向目录的功能。

待续。。。。


本文在 CSDN上BLOG:
    http://blog.csdn.net/fanxiushu/article/details/43636575 以及后续章节

本文在CSDN上提供的程序:
http://download.csdn.net/detail/fanxiushu/8448785

本文在 CSDN上提供的源代码工程:
    http://download.csdn.net/detail/fanxiushu/8545567


2017-01更新,完整版本程序下载地址:

CSDN上的下载地址:

http://download.csdn.net/detail/fanxiushu/9719017

GITHUB上的下载地址:

https://github.com/fanxiushu/xFsRedir/raw/master/xFsRedir-1.0.0.1.zip

这个是1.0.0.1版本,如果寻找新版本,请关注:

https://github.com/fanxiushu/xFsRedir

或发送邮件 fanxiushu@sohu.com 如发现BUG,可在CSDN或者GitHUB上提出来,或者发送邮件 fanxiushu@sohu.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重定向系统各临时文件夹到另外盘符 首先要在“开始”——“运行”内输入“regedit”打开注册表编辑器,然后要在“文件”下拉菜单中的“导出”功能备份好注册表,以防万一,接着在左侧窗口依次打开: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders 在右侧窗口里,你看到的“名称”就代表那些特殊的文件夹,“数据”就是它们所对应的默认存储路径。修改默认的路径就可以让你的桌面文件夹移动了,下面是所有的. 下面介绍一下各个“名称”所代表的文件夹 名称 含义 默认路径 AppData 应用程序数据目录 C:\Documents and Settings\User name\Application Data Cookies Cookies路径 C:\Documents and Settings\User name\Cookies Desktop 桌面路径 C:\Documents and Settings\User name\桌面 Favorites 收藏夹 C:\Documents and Settings\User name\Favorites NetHood NetHood路径 C:\Documents and Settings\User name\NetHood Personal 我的文档 C:\Documents and Settings\User name\My Documents PrintHood 打印 C:\Documents and Settings\User name\PrintHood Recent 文档项路径 C:\Documents and Settings\User name\Recent SendTo SendTo路径 C:\Documents and Settings\User name\SendTo Start Menu 开始菜单路径 C:\Documents and Settings\User name\「开始」菜单 Templates 新建文件目录 C:\Documents and Settings\User name\Templates Programs 程序菜单路径 C:\Documents and Settings\User name\「开始」菜单\程序 Startup 启动路径 C:\Documents and Settings\User name\「开始」菜单\程序\启动 History 网页历史记录 C:\Documents and Settings\ User name \Local Settings\History My Pictures 图片收藏 C:\Documents and Settings\User name\My Documents\My Pictures My Music 我的音乐 C:\Documents and Settings\User name\My Documents\My Music My Video 我的视频 C:\Documents and Settings\User name\My Documents\My Videos Cache Internet临时文件夹 C:\ Documents and Settings\User name \Temporary Internet Files 这些文件夹称为Shell文件夹 其中“User name”为当前用户的名称 了解了对应的文件夹,就可以根据自己的需要去更改对应的路径了。(千万不要在这儿改啊,那样可就瞎忙乎了,系统重起后它会恢复成原来的路径) 在同一层中你可以看到一个“User Shell Folders”的子键,即在HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ User Shell Folders 这里包括了用户定制的所有Shell文件夹的值项。只要通过修改“数据”,就可以改变它们的存储路径。双击需要修改的名称,在弹出的“编辑字符串”的“数值数据”里填上你要更改的完整路径,按下“确定”就完成了。如果没有你需要的,可以在右边窗口单击鼠标右键,选择“新建”菜单中的“字符串值”命令,对应上表,添加一个用于Shell文件夹的字符串值。 在上面的文件夹中,并没有outlook的通讯簿和邮件存放路径,它们分别在 通讯簿路径: HKEY_CURRENT_USER\Software\Microsoft\WAB\WAB4\Wab File Name主键下,将
### 回答1: kmsgcat-log是一个Linux命令,用于将内核消息缓冲区中的消息输出到终端或日志文件中。kmsgcat-log通常用于系统故障排除和调试,可以帮助开发人员和系统管理员查看系统崩溃和错误信息。 使用kmsgcat-log命令可以输出当前内核消息缓冲区的所有消息,也可以使用grep等工具来过滤输出的消息。以下是kmsgcat-log的基本用法示例: ``` sudo kmsgcat-log > kmsg.txt # 将内核消息缓冲区的消息输出到文件kmsg.txt中 sudo kmsgcat-log | grep error # 过滤输出包含"error"的消息 ``` 需要注意的是,kmsgcat-log命令需要使用root权限才能执行。 ### 回答2: kmsgcat-log是一个在Linux系统中用来查看内核日志的命令行工具。kmsgcat-log可以通过读取特殊的内核日志设备/var/log/kmsg来获取内核输出的日志信息。 kmsgcat-log能够以易读的形式显示内核日志,包括内核启动信息、硬件错误、内存分配和释放、设备驱动信息等。通过使用kmsgcat-log,系统管理员或开发人员可以更方便地查看和分析内核的运行状况。 使用kmsgcat-log的命令格式为kmsgcat-log [OPTIONS] [FILE],其中可选项包括-v(显示详细日志信息)、-n NUM(仅显示最后NUM条日志)、-d(显示时间戳)、-t(显示日志标签)等。 除了直接在命令行中使用kmsgcat-log,也可以将输出结果重定向文件中进行保存。例如,kmsgcat-log > kernel_log.txt将内核日志保存到kernel_log.txt文件中。 总的来说,kmsgcat-log是一个方便的工具,可以帮助我们更好地了解和监控Linux系统的内核运行情况。无论是系统管理员还是开发人员都可以通过使用kmsgcat-log来查找问题、分析故障和监测系统性能。 ### 回答3: kmsgcat-log是Android系统中的一个命令行工具,用于查看和分析内核消息。在Android中,内核消息通常存储在一个称为kmsg缓冲区的特殊文件中。通过使用kmsgcat-log工具,可以将kmsg缓冲区中存储的内核消息打印到终端或保存到文件中,以便进行后续的分析。 kmsgcat-log命令有多个参数可以使用。其中,最常用的参数是"-t",用于指定打印的消息类型,如内核调试信息、警告信息、错误信息等。另外,还可以使用"-f"参数指定输出到文件中,使用"-d"参数指定输出到终端中,并可以使用"-s"参数指定输出消息的大小限制。除了这些基本的参数外,还有一些其他的可选参数可以用于进一步帮助分析内核消息。 使用kmsgcat-log可以帮助开发者和系统管理员跟踪和诊断Android系统中的各种内核问题。通过查看内核消息,可以了解到系统启动时、运行时或关机时的各种事件和异常情况,从而更好地了解系统的运行状态。对于开发人员来说,这对于调试崩溃问题、性能问题和其他系统问题等非常有帮助。对于系统管理员来说,可以通过分析内核消息来监控系统的稳定性和安全性,及时处理系统中出现的异常情况。 总之,kmsgcat-log是一个方便的工具,通过它可以很方便地查看和分析Android系统中的内核消息,帮助我们更好地了解系统的运行状况和排查问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值