目录
对最近学习Fanotify的一些理解
Fanotify (fscking all notifiction and file access system) 是一个 notifier,即一种对文件系统变化产生通知的机制。根据man7手册的阅读,可以知道fanotify是inotify的一个新进版本,主要是用于文件系统扫描的检测和分层存储的管理。最近几年对fanotify的完善也是很快的,查看了一下源码可以看出来fanotify支持的文件系统事件已经比inotify多了。个人认为fanotify与inotify最大区别是fanotify加入了打开关闭等事件的许可判断。即在打开或者关闭文件之前,需要程序员注册一个函数,根据程序所需要去判断是否允许打开文件或者关闭文件,然后将判断的结果再写入内核中,此时内核会执行该结果(相当于Ring0级别的Hook)。很明显,这几个文件系统事件可以用来实现一个文件监测控制系统,除了文件系统的扫描以外还可以控制文件的打开关闭等操作。很符合杀毒软件的开发。
Fanotify知识总结
fanotify基本功能
-
文件系统事件的通知
这个功能和inotify的功能是一样的,即监听一些普遍的文件系统事件,例如读写打开关闭等操作的发生。当这些事件发生后,fanotify就会通知程序员发生了什么事件。 -
实现文件系统的监测管控
该功能是实现全文件的系统的监测。相对于inotify来讲,fanotify在该功能上更具备扩展性。inotify在进行监控的时候,是需要一个wd对象进行监控的,所以当文件系统一复杂,wd的维护就变得麻烦。而fanotify所支持的三种模式可以使得该管理变得简洁。
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,有一点儿总比完全没有好一些吧。
- 访问控制
访问控制这个功能是之前inotify没有的,这是fanotify和inotify之间最大的不同之处。fanotify增加了访问控制的事件,例如:FAN_OPEN_PERM、FAN_CLOSE_PERM等,这些事件需要程序员通过程序需要判断该文件是否被允许打开或者关闭操作,并把该决策向内核进行写入和注册,最后让系统调用返回该结果。 - 监听者级别的划分
该功能便是允许多个Listener监听同一个文件对象,并且可以设置Listener的级别。fanotify将所有的Listener设置了三个group,其优先从高到低分别为:FAN_CLASS_PRE_CONTENT、FAN_CLASS_CONTENT、FAN_CLASS_NOTIF
从上述宏的命名也大致可知:FAN_CLASS_PRE_CONTENT 用于 HSM 等需要在应用程序使用文件的 CONTENT 之前就得到文件操作权的应用程序;FAN_CLASS_CONTENT 适用于杀毒软件等需要检查文件 CONTENT 的软件;而 FAN_CLASS_NOTIF 则用于纯粹的 notification 软件,不需要访问文件内容的应用程序。
-
监听者程序的PID过滤
监听者程序也可能会触发fanotify中的事件,在fanotify中,如果触发者是Listener的PID的话,fanotify就会忽略该事件,避免两者进入死循环。能够实现该过滤的原因是fanotify在事件中包含了Listener的PID,而inotify并不具备该功能。 -
缓存机制
例如一个文件没有进行任何修改,但是每次都需要扫描监测的话会很麻烦,这时候fanotify的缓存机制就有很好的体现,假设它对某个文件对象设置了ignore mask标志位的话,只会对该对象进行一次扫描,之后如果该文件对象没有进行任何修改的话,它的打开关闭操作就会正常执行,fanotify会忽略访问控制事件,始终允许访问。如果进行了修改的话,fanotify会自动清除它的ignore mask标志位,然后对它进行正常的访问控制等事件。
Fanotify 支持这种 cache,也叫做 ignore marks。它的工作原理很简单,假如对一个文件系统对象设置了 ignore marks,那么下次该文件被访问时,相应的事件便不会触发访问控制的代码,从而始终允许该文件的访问。
杀毒软件可以这样使用此特性,当应用程序第一次打开文件 file A 时,Fanotify 将通知杀毒软件 AV 进行文件内容扫描,如果 AV 软件发现该文件没有病毒,在允许本次访问的同时,对该文件设置一个 ignore mark。
fanotify类型函数
fanotify_init()
#include <fcntl.h>
#include <sys/fanotify.h>
int fanotify_init(unsigned int flags, unsigned int event_f_flags);
该函数初始化fanotify事件组,并且该fanotify组的事件队列的int类型句柄。它的另一个优势,在这里可以看出,可以通过epoll、select、kqueue等监听。
第一个flags参数包含一个多位字段,该字段定义侦听应用程序的通知类,并进一步包含一个位字段,指定文件描述符的行为。其中包括:
FAN_CLASS_PRE_CONTENT:
此值允许接收通知文件已被访问的事件,以及可能访问文件时用于权限决策的事件。它适用于需要在包含最终数据之前访问文件的事件侦听器。例如,分层存储管理器可能使用这个通知类。
FAN_CLASS_CONTENT:
此值允许接收通知文件已被访问的事件,以及可能访问文件时用于权限决策的事件。它是为那些需要访问已经包含最终内容的文件的事件侦听器而设计的。例如,恶意软件检测程序可能会使用这个通知类。
FAN_REPORT_FID (since Linux 5.1):
此值允许接收包含有关与事件关联的底层文件系统对象的附加信息的事件。附加结构封装了关于对象的信息,并与通用事件元数据结构一起包含。用来表示与事件相关的对象的文件描述符被替换为文件句柄。它适用于可能发现使用文件句柄标识对象比使用文件描述符更合适的应用程序。此外,它还可以用于对目录条目事件感兴趣的应用程序,例如FAN_CREATE、FAN_ATTRIB、FAN_MOVE和FAN_DELETE。注意,在监视挂载点时不支持使用目录修改事件。此标志不允许使用FAN_CLASS_CONTENT或FAN_CLASS_PRE_CONTENT,并将导致错误EINVAL。更多信息请参见fanotify(7)。
FAN_CLASS_NOTIF(默认值):
这是默认值。它不需要指定。此值只允许接收通知文件已被访问的事件。不可能在访问文件之前做出权限决定。
第二个参数和open函数的第二个参数意义相同。event_f_flags参数定义将在为fanotify事件创建的打开文件描述上设置的文件状态标志。有关这些标志的详细信息,请参见open(2)中的标志值描述。event_f_flags包含一个用于访问模式的多位字段。该字段可以取以下值:O_RDONLY、O_WRONLY、O_RDWR.
fanotify_mark
#include <sys/fanotify.h>
int fanotify_mark(int fanotify_fd, unsigned int flags,
uint64_t mask, int dirfd, const char *pathname);
fanotify_mark()在文件系统对象上添加、删除或修改fanotify标记。调用者必须对要标记的文件系统对象具有读权限。
第一个参数fanotify_init函数的返回值,第二个参数标志位是描述药执行的修改,它包含很多数值,具体参考man 7手册查看。
man 7 fanotify_mark参数含义
fanotify使用
运用中的问题
由于这次是因为比赛接触到的fanotify,比赛的环境是龙芯的机子,深度的操作系统。其中我们运用FAN_OPEN_PERM的时候是被操作系统视为无效参数,经过查看.config文件发现,其中的一些参数是不允许开启的,虽然经过修改等进行内核重编但是也没有在该机子上实现FAN_OPEN_PERM的使用。这个问题以目前的水平未能追到底。不知道是什么原因导致fanotify的可移植性差。