Board logo

标题: fanotify 监控文件系统(2) [打印本页]

作者: look_w    时间: 2018-4-23 15:54     标题: fanotify 监控文件系统(2)

全文件系统监控Inotify使用watchdescriptor这个数据结构来对应某个被监控的文件或者目录。每个需要被监控的文件系统对象(文件,目录)都需要一个wd对象来表示。对于下面图 2 中的文件树,您可以看到一个复杂文件目录需要维护多少wd。
图 2. 测试如果是需要对整个文件系统进行监控呢(比如杀毒软件)?需要创建多少的 wd 啊!在画上面这个图的时候,红色的箭头线让我非常疲惫。因此人们常说inotify缺乏可扩展性,因为如果再多一些箭头,我将拒绝再画了。
Fanotify 有三个个基本的模式:directed,per-mount 和 global。其中,directed 模式和 inotify 类似,直接工作在被监控的对象的 inode 上,一次只可以监控一个对象。因此需要监控大量目标时也很麻烦。Global 模式则监控整个文件系统,任何变化都会通知 Listener。杀毒软件便工作在这种模式下。Per-mount 模式工作在 mount 点上,比如磁盘 /dev/sda2 的 mount 点在 /home,则 /home 目录下的所有文件系统变化都可以被监控,这其实可以被看作另外一种 Global 模式。
长期以来,人们都希望 Linux 的 notifier 可以支持 sub-tree 通知,比如图 2 的众多监控对象都在 /home 目录下面,假如 notifier 可以指定监控整个 /home 目录,其下任意文件或者子目录的变化都可以引起通知,监控程序便无需为每一个 /home 下面的子目录和文件一一添加 watch descriptor 了。
在很久以前,Fanotify 就暗示说实现 sub-tree notification 不是不可能的,但直到今天 fanotify 依然无法支持 sub-tree 监控。但比 inotify 进了一步的是,fanotify 可以监控某个目录下的直接子节点。比如可以监控 /home 和他的直接子节点,文件 /home/foo1,/home/foo2 等都可以被监控,但 /home/pics/foo1 就不可以了,因为 /home/pics/foo1 不是 /home 的直接子节点。希望在后续的 fanotify 版本中可以弥补这个不足。
面对 sub-tree 监控的需要,目前 fanotify 的折中方案是采用 Global 模式,然后在监控程序内部进行判断,剔除那些不感兴趣的文件系统对象。这虽然不完美,但也算一个可行的方案吧。相比 inotify,有一点儿总比完全没有好一些吧。
访问控制 Access decision在文件系统通知这个方面,fanotify 做的并不比 inotify 好,甚至还更差一些,因为 inotify 能监控更多类型的变化。我觉得非要说 fanotify 是 inotify 的替代,Access Decision 功能是一个比较有说服力的特性。Inotify 不能提供这个能力。
所谓 access descision 即当文件被访问的时候,监控程序不仅可以获得这个事件通知,还能够决定是否允许该操作。这对于杀毒软件是必要的:当您试图打开一个含有病毒的文件时,fanotify 将产生一个通知给作为 listener 的杀毒软件,这个时候杀毒软件不仅需要判断将被打开的文件是否含有病毒,还需要阻止您的这个不安全的操作。否则杀毒软件在检查到病毒的时候只能说:“哎呀,您中毒了!”因为病毒文件还是被打开了。
图 3. Access decision 流程上图演示了 fanotify 进行访问控制的流程。当 app 需要打开文件的时候,加入该文件已经被 AV 程序监控,那么 open 这个操作将引起 fanotify 的通知,在 VFS 允许 open 返回之前,fanotify 先询问 AV program,假如允许,则 app 的 open 调用成功,否则 app 的 open 调用将失败。这样就可以阻止应用程序打开带病毒的文件了。
Listener groupsFanotify 允许多个 Listener 同时监控同一个文件系统对象。比如杀毒软件 V 和桌面搜索软件 S 会同时监控目录 /myDocument。当文件 /mydocument/test 被打开的时候,fanotify 将通知 V 和 S。那么先通知谁呢?您可能觉得无关紧要,但实际上有时候这个很重要。
比如有一类软件叫做 hierarchical storage manager(HSM),在文件系统中实际存放的可能只是一个 stub 文件,文件真正的内容在下一级存储设备中,因此当 stub 文件被打开时,fanotify 应该先通知 HSM,让它先工作,将真正的文件内容导入到 stub 文件中;然后再通知杀毒软件,对真正的文件内容进行扫描;否则就有这样的一种可能:杀毒文件只扫描了 stub,而 HSM 随后将病毒导入。
Fanotify 将所有的 Listener 分成三个 Group,优先级从上到下递减 :
初始化为 FAN_CLASS_PRE_CONTENT 的 Listener 优先级最高,将最先收到通知,其后是 FAN_CLASS_CONTENT;最后才是 FAN_CLASS_NOTIF 进程得到通知。
从上述宏的命名也大致可知:FAN_CLASS_PRE_CONTENT 用于 HSM 等需要在应用程序使用文件的 CONTENT 之前就得到文件操作权的应用程序;FAN_CLASS_CONTENT 适用于杀毒软件等需要检查文件 CONTENT 的软件;而 FAN_CLASS_NOTIF 则用于纯粹的 notification 软件,不需要访问文件内容的应用程序。
Listener PID调用 Inotify 进行监控的进程如果对被监控文件进行操作,也将引起通知。有时候这会造成问题:
考察下面这个例子程序:
清单 1. inotify 的 loop 问题
1
2
3
4
5
6
7
8
9
10
inotify_add_watch (fd, “/home/lm/loop”
          IN_MODIFY | IN_OPEN | IN_CREATE | IN_DELETE);
// 监控文件 /home/lm/loop

for (;;)
{
  readInotifyEvent();
  if(event->mask & IN_OPEN)
      check_what_changed(event); // 检查有些什么改动
}




函数 check_what_changed() 为了检查文件内容是否有变化必须调用 open 打开文件。
清单 2. 检查文件内容是否有变化
1
2
3
4
5
6
void check_what_changed(event)
{
fd = open(event->name, O_RDWR); // 又触发 inotify 通知
read (fd, buf,128)

}




这里的 open 操作也会触发 inotify 通知,从而使得代码清单 1 的 for 循环成为一个很难打破的无限循环。
Fanotify 在通知中包含了触发事件的进程的 Pid,因此上面的问题可以轻易解决:
在 check_what_changed 函数中判断引起通知的 pid,如果是监控程序自己,则忽略这个通知,不会再次打开该文件。从而打破无限循环。
Inotify 在事件中不包含 pid,因此监控程序无法知道是哪个进程触发了文件系统事件,这对于某些应用也是不方便的。Fanotify 在通知中包含了 Pid 从而满足了某些应用的需要。
实际上,Fanotify 的通知中包含了被监控文件系统对象的 open fd,应用程序可以直接使用这个 fd 对文件对象进行操作,而不会引起新的通知。这也是 Fanotify 相对于 Inotify 改进的一个地方。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0