fanotify实现原理源码分析

fanotify实现原理源码分析

fanotify是一种文件监控的技术,一种比较主流的应用场景就是杀毒软件的文件扫描(或者其他安全软件对于文档的监控),当文件修改或者创建的时候收到相关事件。如下是一个fanotify的基本使用示例:

#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <limits.h>
#include <sys/fanotify.h>
#include <errno.h>
#include <unistd.h>

static void handle_events(int fd)
{
	char buffer[4096] = { 0 };
	const struct fanotify_event_metadata* metadata = NULL;
	size_t len = 0;

	while (1)
	{
		len = read(fd, buffer, sizeof(buffer));
		if (len == (size_t)-1)
		{
			break;
		}

		if (len == 0)
		{
			break;
		}

		metadata = (const struct fanotify_event_metadata*)(buffer);

		while (FAN_EVENT_OK(metadata, len))
		{
			if (metadata->vers != FANOTIFY_METADATA_VERSION)
			{
				break;
			}

			if (metadata->fd >= 0)
			{
				char file_path[PATH_MAX] = { 0 };
				char fd_path[PATH_MAX] = { 0 };

				if (metadata->mask & FAN_OPEN_PERM)
				{
					struct fanotify_response response;
					response.fd = metadata->fd;
					response.response = FAN_ALLOW;
					write(fd, &response, sizeof(response));
					printf("FAN_OPEN_PERM: ");
				}

				if (metadata->mask & FAN_CLOSE_WRITE)
				{
					printf("FAN_CLOSE_WRITE: ");
				}
				snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", metadata->fd);
				(void)readlink(fd_path, file_path, sizeof(file_path) - 1);
				printf("%s.\n", file_path);

				close(metadata->fd);
			}

			metadata = FAN_EVENT_NEXT(metadata, len);
		}

	}
}
int main(int argc, char* argv[])
{
	int fd = -1;
	int ret = 0;
	struct pollfd fds[1] = { 0 };

	if (argc != 2)
	{
		return -1;
	}

	fd = fanotify_init(FAN_CLASS_CONTENT | FAN_CLOEXEC | FAN_NONBLOCK, O_RDONLY);
	if (-1 == fd)
	{
		return -1;
	}

	ret = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD, argv[1]);
	if (ret != 0)
	{
		close(fd);
		return -1;
	}

	fds[0].fd = fd;
	fds[0].events = POLLIN;

	while (1)
	{
		int poll_num = poll(fds, 1, -1);
		if (poll_num == -1)
		{
			break;
		}
		if (poll_num > 0)
		{
			if (fds[0].events & POLLIN)
			{
				handle_events(fd);
			}
		}
	}
	
	close(fd);
	fd = -1;

	return ret;
}

从上面我们可以发现,fanotify的使用是非常简单的,那么fanotify具体是怎么被实现的呢?下面通过源码来看一下fanotify的实现原理。

1. fanotify_init

1.1 使用

fanotify_init函数的声明如下:

int fanotify_init(unsigned int flags, unsigned int event_f_flags);

这个函数返回一个fsnotify_group的文件描述符,这个函数有两个参数:

  1. flags用来设置fsnotify_group的相关属性。
  2. event_f_flags这个是对通用的文件描述符的标志属性。

因此flags才是我们熟悉fanotify_init使用的关键,这个参数分为两组:

  1. fanotify_init, fanotify_initfanotify_init表示event(文件消息)的通知时机。
  2. FAN_CLOEXEC, FAN_NONBLOCK, FAN_UNLIMITED_QUEUEFAN_UNLIMITED_MARKS是其他的附加标记。

对于每个成员的取值含义,可以参加帮助文件,fanotify_init的使用如下:

fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_CLOEXEC | FAN_NONBLOCK, O_RDONLY);

1.2 fanotify_init

fanotify_init在Linux中的函数响应为:

SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)

这个函数的实现如下:

SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{
	struct fsnotify_group *group;
	int f_flags, fd;
	struct user_struct *user;
	struct fanotify_event *oevent;

	/*
	1. 是否拥有管理员权限
	2. 从这里可以发现fanotify_init的调用,需要管理员权限的支持
	*/
	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	/*
	1. flags所支持的位判断。
	2. CONFIG_AUDITSYSCALL可以发现,审计信息支持配置是否支持
	3. event_f_flags所支持位的判断
	*/
#ifdef CONFIG_AUDITSYSCALL
	if (flags & ~(FANOTIFY_INIT_FLAGS | FAN_ENABLE_AUDIT))
#else
	if (flags & ~FANOTIFY_INIT_FLAGS)
#endif
		return -EINVAL;

	if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
		return -EINVAL;

	switch (event_f_flags & O_ACCMODE) {
	case O_RDONLY:
	case O_RDWR:
	case O_WRONLY:
		break;
	default:
		return -EINVAL;
	}

	if ((flags & FAN_REPORT_FID) &&
		(flags & FANOTIFY_CLASS_BITS) != FAN_CLASS_NOTIF)
		return -EINVAL;

	/*
	1. 判断每个用户注册的listeners是否超过了FANOTIFY_DEFAULT_MAX_LISTENERS
	*/
	user = get_current_user();
	if (atomic_read(&user->fanotify_listeners) > FANOTIFY_DEFAULT_MAX_LISTENERS) {
		free_uid(user);
		return -EMFILE;
	}

	/*
	1. fd的标记
	*/
	f_flags = O_RDWR | FMODE_NONOTIFY;
	if (flags & FAN_CLOEXEC)
		f_flags |= O_CLOEXEC;
	if (flags & FAN_NONBLOCK)
		f_flags |= O_NONBLOCK;

	/*
	1. 使用fsnotify_alloc_group分配一个struct fsnotify_group内存。
	2. 并且做简单的初始化
	*/
	group = fsnotify_alloc_group(&fanotify_fsnotify_ops);
	if (IS_ERR(group)) {
		free_uid(user);
		return PTR_ERR(group);
	}

	/*
	1. fsnotify_group赋予给用户
	2. 给fsnotify_group一个独立的mem_cgroup。
	3. 做其他相关初始化
	*/
	group->fanotify_data.user = user;
	group->fanotify_data.flags = flags;
	atomic_inc(&user->fanotify_listeners);
	group->memcg = get_mem_cgroup_from_mm(current->mm);

	oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL,
		FSNOTIFY_EVENT_NONE, NULL);
	if (unlikely(!oevent)) {
		fd = -ENOMEM;
		goto out_destroy_group;
	}
	group->overflow_event = &oevent->fse;

	if (force_o_largefile())
		event_f_flags |= O_LARGEFILE;
	group->fanotify_data.f_flags = event_f_flags;
	init_waitqueue_head(&group->fanotify_data.access_waitq);
	INIT_LIST_HEAD(&group->fanotify_data.access_list);

	/*
	1. 设置好通知的级别
	*/
	switch (flags & FANOTIFY_CLASS_BITS) {
	case FAN_CLASS_NOTIF:
		group->priority = FS_PRIO_0;
		break;
	case FAN_CLASS_CONTENT:
		group->priority = FS_PRIO_1;
		break;
	case FAN_CLASS_PRE_CONTENT:
		group->priority = FS_PRIO_2;
		break;
	default:
		fd = -EINVAL;
		goto out_destroy_group;
	}

	/*
	1. event队列大小和mark的大小
	*/
	if (flags & FAN_UNLIMITED_QUEUE) {
		fd = -EPERM;
		if (!capable(CAP_SYS_ADMIN))
			goto out_destroy_group;
		group->max_events = UINT_MAX;
	}
	else {
		group->max_events = FANOTIFY_DEFAULT_MAX_EVENTS;
	}

	if (flags & FAN_UNLIMITED_MARKS) {
		fd = -EPERM;
		if (!capable(CAP_SYS_ADMIN))
			goto out_destroy_group;
		group->fanotify_data.max_marks = UINT_MAX;
	}
	else {
		group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS;
	}

	if (flags & FAN_ENABLE_AUDIT) {
		fd = -EPERM;
		if (!capable(CAP_AUDIT_WRITE))
			goto out_destroy_group;
	}

	//分配一个fd
	fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
	if (fd < 0)
		goto out_destroy_group;

	return fd;

out_destroy_group:
	fsnotify_destroy_group(group);
	return fd;
}

这个函数的主要流程为:

  1. 简要相关权限以及各种参数。
  2. 分配struct fsnotify_group结构体,并初始化。
  3. 使用anon_inode_getfd创建一个匿名的文件描述符。

例如我们可以看到如下的文件描述符:

$ sudo ls -al /proc/3797/fd | grep anon
lrwx------. 1 root root 64 Jun 20 22:04 3 -> anon_inode:[fanotify]

2. fanotify_mark

2.1 使用

fanotify_mark函数的原型声明如下:

int fanotify_mark(int fanotify_fd, unsigned int flags,
                    uint64_t mask, int dirfd, const char *pathname);

对于这个函数的参数如下:

  1. fanotify_fd为使用fanotify_init创建的文件描述符,表示这个函数操作的struct fsnotify_group结构体对象。
  2. flags表示怎么操作,有两种参数选择:
    1. FAN_MARK_ADD, FAN_MARK_REMOVE 以及 FAN_MARK_FLUSH表示怎么操作(增加,移除,还是清理所有规则)。
    2. FAN_MARK_DONT_FOLLOW, FAN_MARK_ONLYDIR, FAN_MARK_MOUNT 以及 FAN_MARK_IGNORED_MASK,对于这些选项的意义可以参考帮助文档。
  3. mask表示对文件事件的监控,例如:FAN_ACCESS, FAN_MODIFY 以及 FAN_CLOSE_WRITE等等。
  4. dirfdpathname表示着文件的路径。

2.2 do_fanotify_mark

fanotify_mark在内核中的系统调用如下:

SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags,
			      __u64, mask, int, dfd,
			      const char  __user *, pathname)
{
	return do_fanotify_mark(fanotify_fd, flags, mask, dfd, pathname);
}

fanotify_mark系统调用主要通过do_fanotify_mark来实现,这个函数实现大致如下:

static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
	int dfd, const char  __user *pathname)
{
	struct inode *inode = NULL;
	struct vfsmount *mnt = NULL;
	struct fsnotify_group *group;
	struct fd f;
	struct path path;
	__kernel_fsid_t __fsid, *fsid = NULL;
	u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
	unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
	int ret;

	/*
	1. 校验falgs和mask的值是否合法。
	*/
	if (mask & ((__u64)0xffffffff << 32))
		return -EINVAL;

	if (flags & ~FANOTIFY_MARK_FLAGS)
		return -EINVAL;

	switch (mark_type) {
	case FAN_MARK_INODE:
	case FAN_MARK_MOUNT:
	case FAN_MARK_FILESYSTEM:
		break;
	default:
		return -EINVAL;
	}

	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
	case FAN_MARK_ADD:		
	case FAN_MARK_REMOVE:
		if (!mask)
			return -EINVAL;
		break;
	case FAN_MARK_FLUSH:
		if (flags & ~(FANOTIFY_MARK_TYPE_BITS | FAN_MARK_FLUSH))
			return -EINVAL;
		break;
	default:
		return -EINVAL;
	}

	if (IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS))
		valid_mask |= FANOTIFY_PERM_EVENTS;

	if (mask & ~valid_mask)
		return -EINVAL;

	f = fdget(fanotify_fd);
	if (unlikely(!f.file))
		return -EBADF;

	/*
	1. 获取文件描述符对应的file
	2. 从file中获取fsnotify_group
	*/
	ret = -EINVAL;
	if (unlikely(f.file->f_op != &fanotify_fops))
		goto fput_and_out;
	group = f.file->private_data;

	
	ret = -EINVAL;
	if (mask & FANOTIFY_PERM_EVENTS &&
		group->priority == FS_PRIO_0)
		goto fput_and_out;

	if (mask & FANOTIFY_INODE_EVENTS &&
		(!FAN_GROUP_FLAG(group, FAN_REPORT_FID) ||
			mark_type == FAN_MARK_MOUNT))
		goto fput_and_out;

	/*
	1. 如果使用FAN_MARK_FLUSH清理标记,直接操作即可(无需文件路径)。
	2. 使用fsnotify_clear_完成清理
	*/
	if (flags & FAN_MARK_FLUSH) {
		ret = 0;
		if (mark_type == FAN_MARK_MOUNT)
			fsnotify_clear_vfsmount_marks_by_group(group);
		else if (mark_type == FAN_MARK_FILESYSTEM)
			fsnotify_clear_sb_marks_by_group(group);
		else
			fsnotify_clear_inode_marks_by_group(group);
		goto fput_and_out;
	}

	//获取文件路径
	ret = fanotify_find_path(dfd, pathname, &path, flags);
	if (ret)
		goto fput_and_out;

	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
		ret = fanotify_test_fid(&path, &__fsid);
		if (ret)
			goto path_put_and_out;

		fsid = &__fsid;
	}

	if (mark_type == FAN_MARK_INODE)
		inode = path.dentry->d_inode;
	else
		mnt = path.mnt;

	/*
	1. 开始做各种mark操作
	2. 对于FAN_MARK_ADD添加目录使用fanotify_add_xxx来添加路径信息。
	3. 对于FAN_MARK_REMOVE移除目录,使用fanotify_remove_xxx来移除标记
	*/
	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
	case FAN_MARK_ADD:
		if (mark_type == FAN_MARK_MOUNT)
			ret = fanotify_add_vfsmount_mark(group, mnt, mask,
				flags, fsid);
		else if (mark_type == FAN_MARK_FILESYSTEM)
			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
				flags, fsid);
		else
			ret = fanotify_add_inode_mark(group, inode, mask,
				flags, fsid);
		break;
	case FAN_MARK_REMOVE:
		if (mark_type == FAN_MARK_MOUNT)
			ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
				flags);
		else if (mark_type == FAN_MARK_FILESYSTEM)
			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
				flags);
		else
			ret = fanotify_remove_inode_mark(group, inode, mask,
				flags);
		break;
	default:
		ret = -EINVAL;
	}

path_put_and_out:
	path_put(&path);
fput_and_out:
	fdput(f);
	return ret;
}

这个函数所作的事情主要有3个:

  1. 校验flagsmask各种标记的合法性。
  2. 通过fd获取file结构体,然后获取fsnotify_group.
  3. 对于不同类型的操作,进行不同处理:
    • FAN_MARK_FLUSH使用fsnotify_clear_xxxx进程清理所有mark的路径。
    • FAN_MARK_ADD使用fanotify_add_xxxx将路径加入到fsnotify_group中。
    • FAN_MARK_REMOVE使用fanotify_remove_xxxx来将路径从fsnotify_group中移除。

2.3 fanotify_add_mark

fanotify_add_mark是对应FAN_MARK_ADD标记的操作,主要有如下三种:

  1. FAN_MARK_MOUNT针对挂载点。
  2. FAN_MARK_FILESYSTEM针对文件系统。
  3. 默认,是针对文件的inode。

分别对应如下三种函数操作:

static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
				      struct vfsmount *mnt, __u32 mask,
				      unsigned int flags, __kernel_fsid_t *fsid)
{
	return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
}

static int fanotify_add_sb_mark(struct fsnotify_group *group,
				struct super_block *sb, __u32 mask,
				unsigned int flags, __kernel_fsid_t *fsid)
{
	return fanotify_add_mark(group, &sb->s_fsnotify_marks,
				 FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
}

static int fanotify_add_inode_mark(struct fsnotify_group *group,
				   struct inode *inode, __u32 mask,
				   unsigned int flags, __kernel_fsid_t *fsid)
{
	if ((flags & FAN_MARK_IGNORED_MASK) &&
	    !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
	    inode_is_open_for_write(inode))
		return 0;

	return fanotify_add_mark(group, &inode->i_fsnotify_marks,
				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
}

在上述的struct vfsmount, struct super_block以及struct inode结构中,都存在struct fsnotify_mark_connector表示各自结构的fsnotify_mark哈希表,都使用fanotify_add_mark来进行操作,如下:

static int fanotify_add_mark(struct fsnotify_group *group,
	fsnotify_connp_t *connp, unsigned int type,
	__u32 mask, unsigned int flags,
	__kernel_fsid_t *fsid)
{
	struct fsnotify_mark *fsn_mark;
	__u32 added;

	/*
	1. mutex_lock获取struct fsnotify_group的锁。
	2. fsnotify_find_mark查找当前的struct fsnotify_mark_connector是否有注册好了当前的fsnotify_group。
	3. 如果不存在,那么分配一个新的fanotify_add_new_mark(第一次注册该fsnotify_group)
	4. fanotify_mark_add_to_mask将mask和flags设置到fsnotify_mark中
	*/
	mutex_lock(&group->mark_mutex);
	fsn_mark = fsnotify_find_mark(connp, group);
	if (!fsn_mark) {
		fsn_mark = fanotify_add_new_mark(group, connp, type, fsid);
		if (IS_ERR(fsn_mark)) {
			mutex_unlock(&group->mark_mutex);
			return PTR_ERR(fsn_mark);
		}
	}
	added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
	if (added & ~fsnotify_conn_mask(fsn_mark->connector))
		fsnotify_recalc_mask(fsn_mark->connector);
	mutex_unlock(&group->mark_mutex);

	fsnotify_put_mark(fsn_mark);
	return 0;
}

/*
1. 遍历所有哈希表。
2. 比较mark->group == group,是否已经注册过。
*/
struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp,
					 struct fsnotify_group *group)
{
	struct fsnotify_mark_connector *conn;
	struct fsnotify_mark *mark;

	conn = fsnotify_grab_connector(connp);
	if (!conn)
		return NULL;

	hlist_for_each_entry(mark, &conn->list, obj_list) {
		if (mark->group == group &&
		    (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
			fsnotify_get_mark(mark);
			spin_unlock(&conn->lock);
			return mark;
		}
	}
	spin_unlock(&conn->lock);
	return NULL;
}

/*
1. 设置好fsnotify_mark的标记.
*/
static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
				       __u32 mask,
				       unsigned int flags)
{
	__u32 oldmask = -1;

	spin_lock(&fsn_mark->lock);
	if (!(flags & FAN_MARK_IGNORED_MASK)) {
		oldmask = fsn_mark->mask;
		fsn_mark->mask |= mask;
	} else {
		fsn_mark->ignored_mask |= mask;
		if (flags & FAN_MARK_IGNORED_SURV_MODIFY)
			fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY;
	}
	spin_unlock(&fsn_mark->lock);

	return mask & ~oldmask;
}

总结: 让我们来看一下相关数据结构

struct mount {
    struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
};

struct super_block {
    struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
};
struct vfsmount {
	struct dentry *mnt_root;	
	struct super_block *mnt_sb;	
	int mnt_flags;
} __randomize_layout;

struct inode {
    struct fsnotify_mark_connector __rcu	*i_fsnotify_marks;
};

//list链接向fsnotify_mark的obj_list
struct fsnotify_mark_connector {
	struct hlist_head list;
};

struct fsnotify_mark {
	__u32 mask;
	refcount_t refcnt;
	struct fsnotify_group *group;
	struct list_head g_list;   //链接到fsnotify_group
	spinlock_t lock;
	struct hlist_node obj_list;   //链接到fsnotify_mark_connector
	__u32 ignored_mask;
	unsigned int flags;		
};

从这里看,我们调用fanotify_mark添加一个路径的时候,并不是往struct fsnotify_group结构中添加数据,而是向路径的mount, vfsmount或者inode中添加一个fsnotify_mark成员。

2.4 fanotify_remove_mark

同样fanotify_remove_mark是对应FAN_MARK_REMOVE标记的操作,主要有如下三种:

  1. FAN_MARK_MOUNT针对挂载点。
  2. FAN_MARK_FILESYSTEM针对文件系统。
  3. 默认,是针对文件的inode。

fanotify_remove_mark函数的实现如下:

static int fanotify_remove_mark(struct fsnotify_group *group,
				fsnotify_connp_t *connp, __u32 mask,
				unsigned int flags)
{
	struct fsnotify_mark *fsn_mark = NULL;
	__u32 removed;
	int destroy_mark;

	mutex_lock(&group->mark_mutex);
	fsn_mark = fsnotify_find_mark(connp, group);
	if (!fsn_mark) {
		mutex_unlock(&group->mark_mutex);
		return -ENOENT;
	}

	removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
						 &destroy_mark);
	if (removed & fsnotify_conn_mask(fsn_mark->connector))
		fsnotify_recalc_mask(fsn_mark->connector);
	if (destroy_mark)
		fsnotify_detach_mark(fsn_mark);
	mutex_unlock(&group->mark_mutex);
	if (destroy_mark)
		fsnotify_free_mark(fsn_mark);

	fsnotify_put_mark(fsn_mark);
	return 0;
}

2.5 fsnotify_clear_marks_by_group

我们知道add和remove之后,clear就很简单了,因为struct fsnotify_group有一个list保存了所有的struct fsnotify_mark,因此fsnotify_clear_marks_by_group这个函数就比较简单了,就是遍历struct fsnotify_group,然后删除指定类型的struct fsnotify_mark,如下:

void fsnotify_clear_marks_by_group(struct fsnotify_group *group,
				   unsigned int type_mask)
{
	struct fsnotify_mark *lmark, *mark;
	LIST_HEAD(to_free);
	struct list_head *head = &to_free;

	if (type_mask == FSNOTIFY_OBJ_ALL_TYPES_MASK) {
		head = &group->marks_list;
		goto clear;
	}

	mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
	list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) {
		if ((1U << mark->connector->type) & type_mask)
			list_move(&mark->g_list, &to_free);
	}
	mutex_unlock(&group->mark_mutex);

clear:
	while (1) {
		mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
		if (list_empty(head)) {
			mutex_unlock(&group->mark_mutex);
			break;
		}
		mark = list_first_entry(head, struct fsnotify_mark, g_list);
		fsnotify_get_mark(mark);
		fsnotify_detach_mark(mark);
		mutex_unlock(&group->mark_mutex);
		fsnotify_free_mark(mark);
		fsnotify_put_mark(mark);
	}
}

3. fsnotify

什么时候会导致fsnotify事件的产生,已经产生的事件怎么分发呢?我们通过open操作来看具体过程,在do_sys_open函数中,如果文件打开成功,那么调用fsnotify_open这个函数实现如下:

static inline void fsnotify_open(struct file *file)
{
	const struct path *path = &file->f_path;
	struct inode *inode = file_inode(file);
	__u32 mask = FS_OPEN;

	if (S_ISDIR(inode->i_mode))
		mask |= FS_ISDIR;
	if (file->f_flags & __FMODE_EXEC)
		mask |= FS_OPEN_EXEC;

	fsnotify_path(inode, path, mask);
}

static inline int fsnotify_path(struct inode *inode, const struct path *path,
	__u32 mask)
{
	//...
	return fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
}

int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
	const struct qstr *file_name, u32 cookie)
{
	//...
	send_to_group(to_tell, mask, data, data_is, cookie,
		file_name, &iter_info);
	//...
}

static int send_to_group(struct inode *to_tell,
	__u32 mask, const void *data,
	int data_is, u32 cookie,
	const struct qstr *file_name,
	struct fsnotify_iter_info *iter_info)
{
	//...
	return group->ops->handle_event(group, to_tell, mask, data, data_is,
		file_name, cookie, iter_info);
}

group->ops->handle_event调用的函数为fanotify_handle_event

3.1 fanotify_handle_event

fanotify_handle_event这个函数对于普通event和perm_event分别进行不同逻辑处理,如下:

static int fanotify_handle_event(struct fsnotify_group *group,
	struct inode *inode,
	u32 mask, const void *data, int data_type,
	const struct qstr *file_name, u32 cookie,
	struct fsnotify_iter_info *iter_info)
{
	int ret = 0;
	struct fanotify_event *event;
	struct fsnotify_event *fsn_event;
	__kernel_fsid_t fsid = {};

	/*
	1. 是否事件需要上报
	*/
	mask = fanotify_group_event_mask(group, iter_info, mask, data,
		data_type);
	if (!mask)
		return 0;

	/*
	1. 是否是perm_event
	2. perm_event需要等待返回结果
	*/
	if (fanotify_is_perm_event(mask)) {

		if (!fsnotify_prepare_user_wait(iter_info))
			return 0;
	}

	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
		fsid = fanotify_get_fsid(iter_info);
		if (!fsid.val[0] && !fsid.val[1])
			return 0;
	}

	/*
	1. 分配一个fanotify_event
	*/
	event = fanotify_alloc_event(group, inode, mask, data, data_type,
		&fsid);
	ret = -ENOMEM;
	if (unlikely(!event)) {
		if (!fanotify_is_perm_event(mask))
			fsnotify_queue_overflow(group);
		goto finish;
	}

	/*
	1. 放入到队列中
	2. 如果是perm_event,获取返回结果
	*/
	fsn_event = &event->fse;
	ret = fsnotify_add_event(group, fsn_event, fanotify_merge);
	if (ret) {
		BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS);
		fsnotify_destroy_event(group, fsn_event);

		ret = 0;
	}
	else if (fanotify_is_perm_event(mask)) {
		ret = fanotify_get_response(group, FANOTIFY_PE(fsn_event),
			iter_info);
	}
finish:
	if (fanotify_is_perm_event(mask))
		fsnotify_finish_user_wait(iter_info);

	return ret;
}

/*
1. 将event放入到队列group->notification_list中
*/
int fsnotify_add_event(struct fsnotify_group *group,
	struct fsnotify_event *event,
	int(*merge)(struct list_head *,
		struct fsnotify_event *))
{
	int ret = 0;
	struct list_head *list = &group->notification_list;

	spin_lock(&group->notification_lock);

	if (group->shutdown) {
		spin_unlock(&group->notification_lock);
		return 2;
	}

	if (event == group->overflow_event ||
		group->q_len >= group->max_events) {
		ret = 2;
		if (!list_empty(&group->overflow_event->list)) {
			spin_unlock(&group->notification_lock);
			return ret;
		}
		event = group->overflow_event;
		goto queue;
	}

	//是否进行合并
	if (!list_empty(list) && merge) {
		ret = merge(list, event);
		if (ret) {
			spin_unlock(&group->notification_lock);
			return ret;
		}
	}

queue:

	//入队
	group->q_len++;
	list_add_tail(&event->list, list);
	spin_unlock(&group->notification_lock);

	//唤醒等待
	wake_up(&group->notification_waitq);
	kill_fasync(&group->fsn_fa, SIGIO, POLL_IN);
	return ret;
}

从上面我们可以发现,event插入对了group->notification_list中了,后续我们的read就是通过fanotify_readgroup->notification_list读取第一个节点,如下:

struct fsnotify_event *fsnotify_peek_first_event(struct fsnotify_group *group)
{
	assert_spin_locked(&group->notification_lock);

	return list_first_entry(&group->notification_list,
				struct fsnotify_event, list);
}

4. 总结

fanotify主要使用struct fsnotify_group来进行管理,对于文件系统,文件和挂载点的mark标记,分别注册在文件系统,文件和挂载点的结构中,示意图如下:
在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值