首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

fanotify 监控文件系统(5)

fanotify 监控文件系统(5)

Fanotify 基本编程接口函数Fanotify 向应用程序提供了两个系统调用:
fanotify_init 和 fanotify_mark,这比之前的 socket 要容易理解很多。
为了在应用程序中使用这两个系统调用,必须自己定义它们作为新的系统调用,因为 fanotify 刚出现不久,目前 glibc 还不支持它。
代码如下:
清单 3,声明系统调用的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#if defined(__x86_64__)
# define __NR_fanotify_init     300
# define __NR_fanotify_mark     301
#elif defined(__i386__)
# define __NR_fanotify_init     338
# define __NR_fanotify_mark     339
#else
# error "System call numbers not defined for this architecture"
#endif

static inline int fanotify_init(unsigned int flags,
          unsigned int event_f_flags)
{
    return syscall(__NR_fanotify_init, flags, event_f_flags);
}

static inline int fanotify_mark(int fanotify_fd,
                unsigned int flags, __u64 mask,
                int dfd, const char *pathname)
{
    return syscall(__NR_fanotify_mark, fanotify_fd,
                     flags, mask,
              dfd, pathname);
}
#endif




演示 fanotify 的例子程序Fanotify 的作者 Eric Paris 写了一个 来演示 fanotify 的使,Eric 写的很好,我写不出更好的了,所以就直接拿来主义吧。在此讲解讲解 Eric 例子程序的细节。
要运行 Eric Paris 的例子程序,您需要 2.6.36 以上的内核。在写这篇文章的时候,我所使用的内核版本为 2.6.39。
为了支持 fanotify,需要选中以下两个内核编译选项:
1
2
FANOTIFY  -- “Filesystem wide access notification”
FANOTIFY_ACCESS_PERMISSIONS -- "fanotify permissions checking"




准备好了内核,就着手看例子代码吧。该例子程序演示 fanotify 的基本功能,我将它编译成可执行文件 av,运行时效果如下:
1
2
3
4
5
6
7
8
9
10
11
[lm@localhost inotify]$ ./av
USAGE: ./av [-cdfhmnp] [-o
{open,close,access,modify,open_perm,access_perm}] file ...
-c: learn about events on children of a directory (not decendants)
-d: send events which happen to directories
-f: set premptive ignores (go faster)
-h: this help screen
-m: place mark on the whole mount point, not just the inode
-n: do not ignore repeated permission checks
-p: check permissions, not just notification
-s N: sleep N seconds before replying to perm events




首先 av 提供 notify 功能,用户可以通过 -o 选项选择需要被通知的事件,比如 open,close 等等,可以不指定而采用默认的通知事件。比如在窗口 1 中运行:
1
./av /home/lm/f1




程序将监控所有对文件 /home/lm/f1 的操作。此时打开新的窗口 2
1
2
cd /hom/lm
echo “test” >f1




对文件进行写操作,此时可以在窗口 1 看到如下输出:
1
/home/lm/f1: pid=2079 open modify close(writable)




-c 选项表示监控当前目录以及子目录的变化,不过需要注意这里只能监控直接子目录,比如
1
./av -c /home/lm




可以监控所有 /home/lm 目录的变化事件,也可以监控 /home/lm/d1 的变化,但不能监控 /home/lm/d1/yetanother 的变化。
-p 表示需要进行访问控制 (Access Decission),目前 Eric 的例子程序默认允许所有的访问,因此假如您运行这个例子程序,表面上看不出和 notify 模式有任何的区别。后面我将在讲解代码之后对访问控制进行小小修改,您便可以看到如何进行访问控制了。
其他的参数不是很重要,限于篇幅,不再赘述。让我们开始解读一些重要的代码片段从而帮助大家理解 fanotify 吧。
初始化 fanotify首先需要初始化 fanotify:
1
fan_fd = fanotify_init(init_flags, O_RDONLY | O_LARGEFILE);




其中 init_flags 根据用户参数的不同而有不同的设置。
清单 4. Init_flags 设置
1
2
3
4
if (fan_mask & FAN_ALL_PERM_EVENTS)
init_flags |= FAN_CLASS_CONTENT;
else
init_flags |= FAN_CLASS_NOTIF;




假如用户指定 -p 选项,即需要 Access Decision, 则将 init_flag 设置为 FAN_CLASS_CONTENT,否则设置为 FAN_CLASS_NOTIF。这个 flag 的含义在 Listener Group 一节中已经讲解过。
除了分组标志之外,可以选择的 init_flag 还有如下一些:
FAN_CLOEXEC
- 设置 close-on-exec 标志,即执行 exec 后,fanotify 的 fd 将被关闭而不能被子进程使用
FAN_NONBLOCK
- 设置 fanotify 的 fd 为非阻塞模式,在其上 read 不会 block,即使没有数据也会立即返回。
FAN_UNLIMITED_QUEUE
- 将 queue depth 设置为无限。即设置无限多的监控对象,使用这个选项必须小心,因为会导致内存用光引发 OOM
FAN_UNLIMITED_MARKS
- 允许应用设置无限多的 Marks,主要是 ignore marks。比如 AV 软件,使用 ignore mark 作为已经扫描过的文件的缓存。因此可能需要很多 marks。同样,使用这个选项需要注意内存问题。
设置监控事件 mask初始化好 Fanotify 之后,就可以告诉 Fanotify 我们想监控哪些文件对象,以及监控哪些事件。这是通过 fanotify_mark 系统调用来实现的。查看例子程序,发现有如下代码:
清单 5. 监控文件系统对象
1
2
3
for (; optind < argc; optind++)
    if (mark_object(fan_fd, argv[optind], AT_FDCWD, fan_mask, mark_flags) != 0)
        goto fail;




av 可以监控多个文件系统对象,只要将这些对象作为命令行参数输入即可,以上循环就是对参数列表所有最后的所有文件系统对象调用 mark_object 函数。其实现如下:
清单 6. 调用 mark_object 函数
1
2
3
4
int mark_object(int fan_fd, const char *path, int fd, uint64_t mask, unsigned int flags)
{
    return fanotify_mark(fan_fd, flags, mask, fd, path);
}




调用 fanotify_mark,对指定文件对象设置 mask。这里有两个重要参数:mask 和 flags。
Mask 表示事件,比如 FAN_ACCESS,详情见表 3.
Flags 有如下这些可能的取值,代表需要进行的操作。
表 3. Flags 可嫩的取值 Flag 标志 flag 含义 FAN_MARK_ADD 添加一个 MASK  FAN_MARK_REMOVE 删除一个 Mask  FAN_MARK_DONT_FOLLOW  same meaning as O_NOFOLLOW as described in open(2)  FAN_MARK_ONLYDIR  same meaning as O_DIRECTORY as described in open(2)  FAN_MARK_MOUNT 工作在 per-mount 模式下,Fanotify 将监控整个 mount 点。 FAN_MARK_IGNORED_MASK 设置一个 ignore mask  FAN_MARK_IGNORED_SURV_MODIFY 当 ignore mask 所对应的 inode 修改时,不清空该 ignore mask。 FAN_MARK_FLUSH 清除所有 Mark
返回列表