This fastpath handler monitors a subtree inside a mount point. To use it: [root] insmod ./fastpath-mod.ko [root] mkdir -p /tmp/a/b/c/d [root] ./fastpath-user /tmp/ /tmp/a/b & [root] touch /tmp/xx # Doesn't generate event [root]# touch /tmp/a/xxa # Doesn't generate event [root]# touch /tmp/a/b/xxab # Generates an event Accessing file xxab # this is the output from fastpath-user [root@]# touch /tmp/a/b/c/xxabc # Generates an event Accessing file xxabc # this is the output from fastpath-user Signed-off-by: Song Liu <song@xxxxxxxxxx> --- MAINTAINERS | 1 + samples/Kconfig | 20 +++++- samples/Makefile | 2 +- samples/fanotify/.gitignore | 1 + samples/fanotify/Makefile | 5 +- samples/fanotify/fastpath-mod.c | 82 +++++++++++++++++++++++ samples/fanotify/fastpath-user.c | 111 +++++++++++++++++++++++++++++++ 7 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 samples/fanotify/fastpath-mod.c create mode 100644 samples/fanotify/fastpath-user.c diff --git a/MAINTAINERS b/MAINTAINERS index 7ad507f49324..8939a48b2d99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8658,6 +8658,7 @@ S: Maintained F: fs/notify/fanotify/ F: include/linux/fanotify.h F: include/uapi/linux/fanotify.h +F: samples/fanotify/ FARADAY FOTG210 USB2 DUAL-ROLE CONTROLLER M: Linus Walleij <linus.walleij@xxxxxxxxxx> diff --git a/samples/Kconfig b/samples/Kconfig index b288d9991d27..b0d3dff48bb0 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -149,15 +149,33 @@ config SAMPLE_CONNECTOR with it. See also Documentation/driver-api/connector.rst +config SAMPLE_FANOTIFY + bool "Build fanotify monitoring sample" + depends on FANOTIFY && CC_CAN_LINK && HEADERS_INSTALL + help + When enabled, this builds samples for fanotify. + There multiple samples for fanotify. Please see the + following configs for more details of these + samples. + config SAMPLE_FANOTIFY_ERROR bool "Build fanotify error monitoring sample" - depends on FANOTIFY && CC_CAN_LINK && HEADERS_INSTALL + depends on SAMPLE_FANOTIFY help When enabled, this builds an example code that uses the FAN_FS_ERROR fanotify mechanism to monitor filesystem errors. See also Documentation/admin-guide/filesystem-monitoring.rst. +config SAMPLE_FANOTIFY_FASTPATH + tristate "Build fanotify fastpath sample" + depends on SAMPLE_FANOTIFY && m + help + When enabled, this builds kernel module that contains a + fanotify fastpath handler. + The fastpath handler filters out certain filename + prefixes for the fanotify user. + config SAMPLE_HIDRAW bool "hidraw sample" depends on CC_CAN_LINK && HEADERS_INSTALL diff --git a/samples/Makefile b/samples/Makefile index b85fa64390c5..108360972626 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -6,7 +6,7 @@ subdir-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs subdir-$(CONFIG_SAMPLE_CGROUP) += cgroup obj-$(CONFIG_SAMPLE_CONFIGFS) += configfs/ obj-$(CONFIG_SAMPLE_CONNECTOR) += connector/ -obj-$(CONFIG_SAMPLE_FANOTIFY_ERROR) += fanotify/ +obj-$(CONFIG_SAMPLE_FANOTIFY) += fanotify/ subdir-$(CONFIG_SAMPLE_HIDRAW) += hidraw obj-$(CONFIG_SAMPLE_HW_BREAKPOINT) += hw_breakpoint/ obj-$(CONFIG_SAMPLE_KDB) += kdb/ diff --git a/samples/fanotify/.gitignore b/samples/fanotify/.gitignore index d74593e8b2de..306e1ddec4e0 100644 --- a/samples/fanotify/.gitignore +++ b/samples/fanotify/.gitignore @@ -1 +1,2 @@ fs-monitor +fastpath-user diff --git a/samples/fanotify/Makefile b/samples/fanotify/Makefile index e20db1bdde3b..f5bbd7380104 100644 --- a/samples/fanotify/Makefile +++ b/samples/fanotify/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -userprogs-always-y += fs-monitor +userprogs-always-$(CONFIG_SAMPLE_FANOTIFY_ERROR) += fs-monitor userccflags += -I usr/include -Wall +obj-$(CONFIG_SAMPLE_FANOTIFY_FASTPATH) += fastpath-mod.o + +userprogs-always-$(CONFIG_SAMPLE_FANOTIFY_FASTPATH) += fastpath-user diff --git a/samples/fanotify/fastpath-mod.c b/samples/fanotify/fastpath-mod.c new file mode 100644 index 000000000000..7e2e1878f8e7 --- /dev/null +++ b/samples/fanotify/fastpath-mod.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/fsnotify.h> +#include <linux/fanotify.h> +#include <linux/module.h> +#include <linux/path.h> +#include <linux/file.h> + +static int sample_fp_handler(struct fsnotify_group *group, + struct fanotify_fastpath_hook *fp_hook, + struct fanotify_fastpath_event *fp_event) +{ + struct dentry *dentry; + struct path *subtree; + + dentry = fsnotify_data_dentry(fp_event->data, fp_event->data_type); + if (!dentry) + return FAN_FP_RET_SEND_TO_USERSPACE; + + subtree = fp_hook->data; + + if (is_subdir(dentry, subtree->dentry)) + return FAN_FP_RET_SEND_TO_USERSPACE; + return FAN_FP_RET_SKIP_EVENT; +} + +static int sample_fp_init(struct fanotify_fastpath_hook *fp_hook, void *args) +{ + struct path *subtree; + struct file *file; + int fd; + + fd = *(int *)args; + + file = fget(fd); + if (!file) + return -EBADF; + subtree = kzalloc(sizeof(struct path), GFP_KERNEL); + if (!subtree) { + fput(file); + return -ENOMEM; + } + path_get(&file->f_path); + *subtree = file->f_path; + fput(file); + fp_hook->data = subtree; + return 0; +} + +static void sample_fp_free(struct fanotify_fastpath_hook *fp_hook) +{ + struct path *subtree = fp_hook->data; + + path_put(subtree); + kfree(subtree); +} + +static struct fanotify_fastpath_ops fan_fp_ignore_a_ops = { + .fp_handler = sample_fp_handler, + .fp_init = sample_fp_init, + .fp_free = sample_fp_free, + .name = "monitor-subtree", + .owner = THIS_MODULE, + .flags = FAN_FP_F_SYS_ADMIN_ONLY, + .desc = "only emit events under a subtree", + .init_args = "struct {\n\tint subtree_fd;\n};", +}; + +static int __init fanotify_fastpath_sample_init(void) +{ + return fanotify_fastpath_register(&fan_fp_ignore_a_ops); +} +static void __exit fanotify_fastpath_sample_exit(void) +{ + fanotify_fastpath_unregister(&fan_fp_ignore_a_ops); +} + +module_init(fanotify_fastpath_sample_init); +module_exit(fanotify_fastpath_sample_exit); + +MODULE_AUTHOR("Song Liu"); +MODULE_DESCRIPTION("Example fanotify fastpath handler"); +MODULE_LICENSE("GPL"); diff --git a/samples/fanotify/fastpath-user.c b/samples/fanotify/fastpath-user.c new file mode 100644 index 000000000000..abe33a6b6b41 --- /dev/null +++ b/samples/fanotify/fastpath-user.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/fanotify.h> +#include <unistd.h> +#include <sys/ioctl.h> + +static int total_event_cnt; + +static void handle_notifications(char *buffer, int len) +{ + struct fanotify_event_metadata *event = + (struct fanotify_event_metadata *) buffer; + struct fanotify_event_info_header *info; + struct fanotify_event_info_fid *fid; + struct file_handle *handle; + char *name; + int off; + + for (; FAN_EVENT_OK(event, len); event = FAN_EVENT_NEXT(event, len)) { + for (off = sizeof(*event) ; off < event->event_len; + off += info->len) { + info = (struct fanotify_event_info_header *) + ((char *) event + off); + switch (info->info_type) { + case FAN_EVENT_INFO_TYPE_DFID_NAME: + fid = (struct fanotify_event_info_fid *) info; + handle = (struct file_handle *)&fid->handle; + name = (char *)handle + sizeof(*handle) + handle->handle_bytes; + + printf("Accessing file %s\n", name); + total_event_cnt++; + break; + default: + break; + } + } + } +} + +int main(int argc, char **argv) +{ + struct fanotify_fastpath_args args = { + .name = "monitor-subtree", + .version = 1, + .flags = 0, + }; + char buffer[BUFSIZ]; + const char *msg; + int fanotify_fd; + int subtree_fd; + + if (argc < 3) { + printf("Usage:\n" + "\t %s <mount point> <subtree to monitor>\n", + argv[0]); + return 1; + } + + subtree_fd = open(argv[2], O_RDONLY | O_CLOEXEC); + + if (subtree_fd < 0) + errx(1, "open subtree_fd"); + + args.init_args = (__u64)&subtree_fd; + args.init_args_size = sizeof(int); + + fanotify_fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_NAME | FAN_REPORT_DIR_FID, + O_RDONLY); + if (fanotify_fd < 0) { + close(subtree_fd); + errx(1, "fanotify_init"); + } + + if (fanotify_mark(fanotify_fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM, + FAN_OPEN | FAN_ONDIR | FAN_EVENT_ON_CHILD, + AT_FDCWD, argv[1])) { + msg = "fanotify_mark"; + goto err_out; + } + + if (ioctl(fanotify_fd, FAN_IOC_ADD_FP, &args)) { + msg = "ioctl"; + goto err_out; + } + + while (total_event_cnt < 10) { + int n = read(fanotify_fd, buffer, BUFSIZ); + + if (n < 0) { + msg = "read"; + goto err_out; + } + + handle_notifications(buffer, n); + } + + ioctl(fanotify_fd, FAN_IOC_DEL_FP); + close(fanotify_fd); + close(subtree_fd); + return 0; + +err_out: + close(fanotify_fd); + close(subtree_fd); + errx(1, msg); + return 0; +} -- 2.43.5