Top / Linux / fanotify

fanotify - filesystem wide access notification

From Linux kernel-2.6.36

IPA Report(japanese)
http://www.ipa.go.jp/security/fy22/reports/tech1-tg/b_03.html
Kernel ML - fanotify: the fscking all notification system
http://lwn.net/Articles/339253/
Kernel ML - fanotify - overall design before I start sending patcheFri, (24 Jul 2009 16:13:4)
http://lwn.net/Articles/343346/
multi-thread opening file hangs when fanotify is on(Sep 27 2011 at 9:15)
http://stackoverflow.com/questions/7566755/multi-thread-opening-file-hangs-when-fanotify-is-on
Kernel ML -[PATCH] fanotify: to differ file access event from different threads
https://lkml.org/lkml/2011/11/17/101

Version ?

KernelMeta data Version
2.6.36 => 2.6.36-4Version 2
2.6.37Version 3
2.6.38Version 3
2.6.39rc1Version 3

Example

for Kernel-2.6.36
from git://git.kernel.org/pub/scm/linux/kernel/git/agruen/fanotify-example.git/

Kernel-2.6.36

fanotify.h - metadata virsion 2

#ifndef _LINUX_FANOTIFY_H
#define _LINUX_FANOTIFY_H

#include <linux/types.h>

/* the following events that user-space can register for */
#define FAN_ACCESS		0x00000001	/* File was accessed */
#define FAN_MODIFY		0x00000002	/* File was modified */
#define FAN_CLOSE_WRITE		0x00000008	/* Unwrittable file closed */
#define FAN_CLOSE_NOWRITE	0x00000010	/* Writtable file closed */
#define FAN_OPEN		0x00000020	/* File was opened */  

#define FAN_EVENT_ON_CHILD	0x08000000	/* interested in child events */ 

/* FIXME currently Q's have no limit.... */
#define FAN_Q_OVERFLOW		0x00004000	/* Event queued overflowed */

#define FAN_OPEN_PERM		0x00010000	/* File open in perm check */
#define FAN_ACCESS_PERM		0x00020000	/* File accessed in perm check */ 

/* helper events */
#define FAN_CLOSE		(FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */

/* flags used for fanotify_init() */
#define FAN_CLOEXEC		0x00000001
#define FAN_NONBLOCK		0x00000002

#define FAN_ALL_INIT_FLAGS	(FAN_CLOEXEC | FAN_NONBLOCK) 

/* flags used for fanotify_modify_mark() */
#define FAN_MARK_ADD		0x00000001
#define FAN_MARK_REMOVE		0x00000002
#define FAN_MARK_DONT_FOLLOW	0x00000004
#define FAN_MARK_ONLYDIR	0x00000008
#define FAN_MARK_MOUNT		0x00000010
#define FAN_MARK_IGNORED_MASK	0x00000020
#define FAN_MARK_IGNORED_SURV_MODIFY	0x00000040
#define FAN_MARK_FLUSH		0x00000080 

#define FAN_ALL_MARK_FLAGS	(FAN_MARK_ADD |\ 
				 FAN_MARK_REMOVE |\ 
				 FAN_MARK_DONT_FOLLOW |\
				 FAN_MARK_ONLYDIR |\
				 FAN_MARK_MOUNT |\
				 FAN_MARK_IGNORED_MASK |\
				 FAN_MARK_IGNORED_SURV_MODIFY)

/*
 * All of the events - we build the list by hand so that we can add flags in
 * the future and not break backward compatibility.  Apps will get only the
 * events that they originally wanted.  Be sure to add new events here!
 */
#define FAN_ALL_EVENTS (FAN_ACCESS |\ 
			FAN_MODIFY |\
			FAN_CLOSE |\
			FAN_OPEN)

/*
 * All events which require a permission response from userspace
 */
#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\
			     FAN_ACCESS_PERM)

#define FAN_ALL_OUTGOING_EVENTS	(FAN_ALL_EVENTS |\
				 FAN_ALL_PERM_EVENTS |\
				 FAN_Q_OVERFLOW)

#define FANOTIFY_METADATA_VERSION	2

struct fanotify_event_metadata {
	__u32 event_len;
	__u32 vers;
	__u64 mask;
	__s32 fd;
	__s32 pid;
} __attribute__ ((packed));

struct fanotify_response {
	__s32 fd;
	__u32 response;
} __attribute__ ((packed));

/* Legit userspace responses to a _PERM event */
#define FAN_ALLOW	0x01
#define FAN_DENY	0x02 

/* Helper functions to deal with fanotify_event_metadata buffers */
#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
				   (struct fanotify_event_metadata*)(((char *)(meta)) + \
				   (meta)->event_len))

#define FAN_EVENT_OK(meta, len)	((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
				(long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
				(long)(meta)->event_len <= (long)(len))

#endif /* _LINUX_FANOTIFY_H */

Kernel-2.6.37 , 38 , 39rc1

fanotify.h - metadata virsion 3

#ifndef _LINUX_FANOTIFY_H
#define _LINUX_FANOTIFY_H

#include <linux/types.h>

/* the following events that user-space can register for */
#define FAN_ACCESS              0x00000001      /* File was accessed */
#define FAN_MODIFY              0x00000002      /* File was modified */
#define FAN_CLOSE_WRITE         0x00000008      /* Writtable file closed */
#define FAN_CLOSE_NOWRITE       0x00000010      /* Unwrittable file closed */
#define FAN_OPEN                0x00000020      /* File was opened */

#define FAN_Q_OVERFLOW          0x00004000      /* Event queued overflowed */

#define FAN_OPEN_PERM           0x00010000      /* File open in perm check */
#define FAN_ACCESS_PERM         0x00020000      /* File accessed in perm check */

#define FAN_ONDIR               0x40000000      /* event occurred against dir */

#define FAN_EVENT_ON_CHILD      0x08000000      /* interested in child events */ 

/* helper events */
#define FAN_CLOSE               (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close  */

/* flags used for fanotify_init() */
#define FAN_CLOEXEC             0x00000001
#define FAN_NONBLOCK            0x00000002

/* These are NOT bitwise flags.  Both bits are used togther.  */
#define FAN_CLASS_NOTIF         0x00000000
#define FAN_CLASS_CONTENT       0x00000004
#define FAN_CLASS_PRE_CONTENT   0x00000008
#define FAN_ALL_CLASS_BITS      (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
                                 FAN_CLASS_PRE_CONTENT)

#define FAN_UNLIMITED_QUEUE     0x00000010
#define FAN_UNLIMITED_MARKS     0x00000020

#define FAN_ALL_INIT_FLAGS      (FAN_CLOEXEC | FAN_NONBLOCK | \
                                 FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
                                 FAN_UNLIMITED_MARKS)

/* flags used for fanotify_modify_mark() */
#define FAN_MARK_ADD            0x00000001
#define FAN_MARK_REMOVE         0x00000002
#define FAN_MARK_DONT_FOLLOW    0x00000004
#define FAN_MARK_ONLYDIR        0x00000008
#define FAN_MARK_MOUNT          0x00000010
#define FAN_MARK_IGNORED_MASK   0x00000020
#define FAN_MARK_IGNORED_SURV_MODIFY    0x00000040
#define FAN_MARK_FLUSH          0x00000080
#ifdef __KERNEL__
/* not valid from userspace, only kernel internal */
#define FAN_MARK_ONDIR          0x00000100
#endif

#define FAN_ALL_MARK_FLAGS      (FAN_MARK_ADD |\
                                 FAN_MARK_REMOVE |\
                                 FAN_MARK_DONT_FOLLOW |\
                                 FAN_MARK_ONLYDIR |\
                                 FAN_MARK_MOUNT |\
                                 FAN_MARK_IGNORED_MASK |\
                                 FAN_MARK_IGNORED_SURV_MODIFY |\
                                 FAN_MARK_FLUSH) 

/*
 * All of the events - we build the list by hand so that we can add flags in
 * the future and not break backward compatibility.  Apps will get only the
 * events that they originally wanted.  Be sure to add new events here! 
 */
#define FAN_ALL_EVENTS (FAN_ACCESS |\
                        FAN_MODIFY |\
                        FAN_CLOSE |\
                        FAN_OPEN)

/*
 * All events which require a permission response from userspace
 */
#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\
                             FAN_ACCESS_PERM)

#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\
                                 FAN_ALL_PERM_EVENTS |\
                                 FAN_Q_OVERFLOW)

#define FANOTIFY_METADATA_VERSION       3 

struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;
        __u8 reserved;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;
        __s32 pid;
};

struct fanotify_response {
        __s32 fd;
        __u32 response;
};

/* Legit userspace responses to a _PERM event */
#define FAN_ALLOW       0x01
#define FAN_DENY        0x02
/* No fd set in event */
#define FAN_NOFD        -1

/* Helper functions to deal with fanotify_event_metadata buffers */
#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
                                   (struct fanotify_event_metadata*)(((char *)(meta)) + \
                                  (meta)->event_len))

#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN &&  \
                                (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
                                (long)(meta)->event_len <= (long)(len))

#endif /* _LINUX_FANOTIFY_H */

Diff of fanotify.h

--- fanotify_2636.h     2011-04-05 17:18:01.509828000 +0900
+++ fanotify_2639rc1.h  2011-04-05 16:42:44.475277000 +0900
@@ -6,18 +6,19 @@
 /* the following events that user-space can register for */
 #define FAN_ACCESS             0x00000001      /* File was accessed */
 #define FAN_MODIFY             0x00000002      /* File was modified */
-#define FAN_CLOSE_WRITE                0x00000008      /* Unwrittable file  closed */
+#define FAN_CLOSE_WRITE                0x00000008      /* Writtable file closed  */
-#define FAN_CLOSE_NOWRITE      0x00000010      /* Writtable file closed */
+#define FAN_CLOSE_NOWRITE      0x00000010      /* Unwrittable file closed */
 #define FAN_OPEN               0x00000020      /* File was opened */ 


 #define FAN_ACCESS_PERM                0x00020000      /* File accessed in perm  check */

+#define FAN_ONDIR              0x40000000      /* event occurred against dir */

 /* helper events */
 #define FAN_CLOSE              (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close  */ 

@@ -25,7 +26,19 @@
 #define FAN_CLOEXEC            0x00000001
 #define FAN_NONBLOCK           0x00000002

+/* These are NOT bitwise flags.  Both bits are used togther.  */
+#define FAN_CLASS_NOTIF                0x00000000
+#define FAN_CLASS_CONTENT      0x00000004
+#define FAN_CLASS_PRE_CONTENT  0x00000008
+#define FAN_ALL_CLASS_BITS     (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
+                                FAN_CLASS_PRE_CONTENT)
+
+#define FAN_UNLIMITED_QUEUE    0x00000010
+#define FAN_UNLIMITED_MARKS    0x00000020
+

-#define FAN_ALL_INIT_FLAGS     (FAN_CLOEXEC | FAN_NONBLOCK)
+#define FAN_ALL_INIT_FLAGS     (FAN_CLOEXEC | FAN_NONBLOCK | \
+                                FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
+                                FAN_UNLIMITED_MARKS)
 /* flags used for fanotify_modify_mark() */
 #define FAN_MARK_ADD           0x00000001
@@ -36,6 +49,10 @@
 #define FAN_MARK_IGNORED_MASK  0x00000020
 #define FAN_MARK_IGNORED_SURV_MODIFY   0x00000040
 #define FAN_MARK_FLUSH         0x00000080
+#ifdef __KERNEL__
+/* not valid from userspace, only kernel internal */
+#define FAN_MARK_ONDIR         0x00000100
+#endif

 #define FAN_ALL_MARK_FLAGS     (FAN_MARK_ADD |\
                                 FAN_MARK_REMOVE |\
@@ -43,7 +60,8 @@
                                 FAN_MARK_ONLYDIR |\
                                 FAN_MARK_MOUNT |\
                                 FAN_MARK_IGNORED_MASK |\
-                                FAN_MARK_IGNORED_SURV_MODIFY)
+                                FAN_MARK_IGNORED_SURV_MODIFY |\
+                                FAN_MARK_FLUSH)

 /*
  * All of the events - we build the list by hand so that we can add flags in
@@ -65,24 +83,28 @@
                                 FAN_ALL_PERM_EVENTS |\
                                 FAN_Q_OVERFLOW) 

-#define FANOTIFY_METADATA_VERSION      2
+#define FANOTIFY_METADATA_VERSION      3

 struct fanotify_event_metadata {
        __u32 event_len;
-       __u32 vers;
+       __u8 vers;
+       __u8 reserved;
+       __u16 metadata_len;
-       __u64 mask;
+       __aligned_u64 mask;
        __s32 fd;
        __s32 pid;
-} __attribute__ ((packed));
+};
 
 struct fanotify_response {
        __s32 fd;
        __u32 response;
-} __attribute__ ((packed));
+};

 /* Legit userspace responses to a _PERM event */
 #define FAN_ALLOW      0x01
 #define FAN_DENY       0x02
+/* No fd set in event */
+#define FAN_NOFD       -1

 /* Helper functions to deal with fanotify_event_metadata buffers */
 #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))

EXAMPLE CODE for metadata version 2

for Kernel-2.6.36
from git://git.kernel.org/pub/scm/linux/kernel/git/agruen/fanotify-example.git/

fanotify.c

#define _ATFILE_SOURCE
#include <errno.h>
#include <inttypes.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <linux/fanotify.h> 

#include "fanotify-syscalllib.h" 

#define FANOTIFY_ARGUMENTS "cfhmn"

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);
}

int set_ignored_mask(int fan_fd, int fd, uint64_t mask)
{ 
	unsigned int flags = (FAN_MARK_ADD | FAN_MARK_IGNORED_MASK);

	return mark_object(fan_fd, NULL, fd, mask, flags);
}

int handle_perm(int fan_fd, struct fanotify_event_metadata *metadata)
{ 
	struct fanotify_response response_struct;
	int ret;

	response_struct.fd = metadata->fd;
	response_struct.response = FAN_ALLOW;

	ret = write(fan_fd, &response_struct, sizeof(response_struct));
	if (ret < 0)
		return ret;

	return 0;
}

void synopsis(const char *progname, int status)
{ 
	FILE *file = status ? stderr : stdout;

	fprintf(file, "USAGE: %s [-" FANOTIFY_ARGUMENTS "] "
		"[-o {open,close,access,modify,open_perm,access_perm}] "
		"file ...\n"
		"-c: learn about events on children of a directory (not decendants)\n"
		"-f: set premptive ignores (go faster)\n"
		"-h: this help screen\n"
		"-m: place mark on the whole mount point, not just the inode\n"
		"-n: do not ignore repeated permission checks\n"
		"-s N: sleep N seconds before replying to perm events\n",
		progname);
	exit(status);
}

int main(int argc, char *argv[])
{ 
	int opt;
	int fan_fd;
	uint64_t fan_mask = FAN_OPEN | FAN_CLOSE | FAN_ACCESS | FAN_MODIFY;
	unsigned int mark_flags = FAN_MARK_ADD;
	bool opt_child, opt_on_mount, opt_fast, opt_ignore_perm;
	int opt_sleep;
	ssize_t len;
	char buf[4096];
	fd_set rfds;


	opt_child = opt_on_mount = opt_fast = false;
	opt_ignore_perm = true;
	opt_sleep = 0;

	while ((opt = getopt(argc, argv, "o:s:"FANOTIFY_ARGUMENTS)) != -1) { 
		switch(opt) {
			case 'o': {
				char *str, *tok;

				fan_mask = 0;
				str = optarg;
				while ((tok = strtok(str, ",")) != NULL) {
					str = NULL;
					if (strcmp(tok, "open") == 0)
 						fan_mask |= FAN_OPEN;
					else if (strcmp(tok, "close") == 0)
						fan_mask |= FAN_CLOSE;
					else if (strcmp(tok, "access") == 0)
						fan_mask |= FAN_ACCESS;
					else if (strcmp(tok, "modify") == 0)
						fan_mask |= FAN_MODIFY;
					else if (strcmp(tok, "open_perm") == 0)
						fan_mask |= FAN_OPEN_PERM;
					else if (strcmp(tok, "access_perm") == 0)
						fan_mask |= FAN_ACCESS_PERM;
					else
						synopsis(argv[0], 1);
				}
				break;
			}
			case 'c':
				opt_child = true;
				break;
			case 'f':
				opt_fast = true;
				opt_ignore_perm = false;
				break;
			case 'm':
   				opt_on_mount = true;
				break;
			case 'n':
 				opt_fast = false;
				opt_ignore_perm = false;
				break;
			case 's':
				opt_sleep = atoi(optarg);
				break;
			case 'h':
				synopsis(argv[0], 0);
			default:  /* '?' */
 				synopsis(argv[0], 1);
		}
	}
	if (optind == argc)
		synopsis(argv[0], 1);

	if (opt_child)
		fan_mask |= FAN_EVENT_ON_CHILD;

	if (opt_on_mount)
		mark_flags |= FAN_MARK_MOUNT;

	fan_fd = fanotify_init(0, O_RDONLY | O_LARGEFILE);
	if (fan_fd < 0)
		goto fail;

	for (; optind < argc; optind++)
 		if (mark_object(fan_fd, argv[optind], AT_FDCWD, fan_mask, m uark_flags) != 0)
			goto fail;

	FD_ZERO(&rfds);
	FD_SET(fan_fd, &rfds);

	if (select(fan_fd+1, &rfds, NULL, NULL, NULL) < 0)
		goto fail;

	while ((len = read(fan_fd, buf, sizeof(buf))) > 0) {
		struct fanotify_event_metadata *metadata;

		metadata = (void *)buf;
		while(FAN_EVENT_OK(metadata, len)) {
			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
				fprintf(stderr, "%s: fanotify version mismatch"
						" (%d != %d)\n",
					argv[0], metadata->vers,
					FANOTIFY_METADATA_VERSION);
				return 1;
			}

			if (metadata->fd >= 0 &&
			    opt_fast &&
			    set_ignored_mask(fan_fd, metadata->fd,
					     FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS))
				goto fail;

			if (metadata->fd >= 0) {
				char path[PATH_MAX];
				int path_len;

				sprintf(path, "/proc/self/fd/%d", metadata->fd);
				path_len = readlink(path, path, sizeof(path)-1);
				if (path_len < 0)
					goto fail;
				path[path_len] = '\0';
				printf("%s:", path);
			} else
				printf("?:");

			printf(" pid=%ld", (long)metadata->pid);

			if (metadata->mask & FAN_ACCESS)
				printf(" access");
			if (metadata->mask & FAN_OPEN)
				printf(" open");
			if (metadata->mask & FAN_MODIFY)
				printf(" modify");
			if (metadata->mask & FAN_CLOSE) {
				if (metadata->mask & FAN_CLOSE_WRITE)
					printf(" close(writable)");
				else
					printf(" close");
			}
			if (metadata->mask & FAN_OPEN_PERM)
				printf(" open_perm");
			if (metadata->mask & FAN_ACCESS_PERM)
				printf(" access_perm");
			if (metadata->mask & FAN_ALL_PERM_EVENTS) {
				if (opt_sleep)
					sleep(opt_sleep);

				if (handle_perm(fan_fd, metadata))
					goto fail;
				if (metadata->fd >= 0 &&
				    opt_ignore_perm &&
				    set_ignored_mask(fan_fd, metadata->fd,
						     metadata->mask))
					goto fail;
			}

			printf("\n");
			fflush(stdout);

			if (metadata->fd >= 0 && close(metadata->fd) != 0)
				goto fail;
			metadata = FAN_EVENT_NEXT(metadata, len);
 		}
 		if (select(fan_fd+1, &rfds, NULL, NULL, NULL) < 0)
 			goto fail;
 	}
 	if (len < 0)
 		goto fail;
 	return 0;
 
 fail:
 	fprintf(stderr, "%s\n", strerror(errno));
 	return 1;
 }

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2012-01-26 (木) 13:01:41 (4262d)