[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <5b8318018dd316f618eea059f610579a205c05db.camel@kernel.org>
Date: Wed, 30 Oct 2024 09:03:27 -0400
From: Jeff Layton <jlayton@...nel.org>
To: Song Liu <song@...nel.org>, bpf@...r.kernel.org,
linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org
Cc: kernel-team@...a.com, andrii@...nel.org, eddyz87@...il.com,
ast@...nel.org, daniel@...earbox.net, martin.lau@...ux.dev,
viro@...iv.linux.org.uk, brauner@...nel.org, jack@...e.cz,
kpsingh@...nel.org, mattbobrowski@...gle.com, amir73il@...il.com,
repnop@...gle.com, josef@...icpanda.com
Subject: Re: [RFC bpf-next fanotify 2/5] samples/fanotify: Add a sample
fanotify fastpath handler
On Tue, 2024-10-29 at 16:12 -0700, Song Liu wrote:
> This fastpath handler filters out events for files with certain prefixes.
> To use it:
>
> [root] insmod fastpath-mod.ko # This requires root.
>
> [user] ./fastpath-user /tmp a,b,c & # Root is not needed
> [user] touch /tmp/aa # a is in the prefix list (a,b,c), no events
> [user] touch /tmp/xx # x is not in the prefix list, generates events
>
> Accessing file xx # this is the output from fastpath_user
>
> Signed-off-by: Song Liu <song@...nel.org>
> ---
> MAINTAINERS | 1 +
> samples/Kconfig | 20 ++++-
> samples/Makefile | 2 +-
> samples/fanotify/.gitignore | 1 +
> samples/fanotify/Makefile | 5 +-
> samples/fanotify/fastpath-mod.c | 138 +++++++++++++++++++++++++++++++
> samples/fanotify/fastpath-user.c | 90 ++++++++++++++++++++
> 7 files changed, 254 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@...aro.org>
> 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..06c4b42ff114
> --- /dev/null
> +++ b/samples/fanotify/fastpath-mod.c
> @@ -0,0 +1,138 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <linux/fsnotify.h>
> +#include <linux/fanotify.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +
> +struct prefix_item {
> + const char *prefix;
> + struct list_head list;
> +};
> +
> +struct sample_fp_data {
> + /*
> + * str_table contains all the prefixes to ignore. For example,
> + * "prefix1\0prefix2\0prefix3"
> + */
> + char *str_table;
> +
> + /* item->prefix points to different prefixes in the str_table. */
> + struct list_head item_list;
> +};
> +
> +static int sample_fp_handler(struct fsnotify_group *group,
> + struct fanotify_fastpath_hook *fp_hook,
> + struct fanotify_fastpath_event *fp_event)
> +{
> + const struct qstr *file_name = fp_event->file_name;
> + struct sample_fp_data *fp_data;
> + struct prefix_item *item;
> +
> + if (!file_name)
> + return FAN_FP_RET_SEND_TO_USERSPACE;
> + fp_data = fp_hook->data;
> +
> + list_for_each_entry(item, &fp_data->item_list, list) {
> + if (strstr(file_name->name, item->prefix) == (char *)file_name->name)
> + return FAN_FP_RET_SKIP_EVENT;
> + }
> +
> + return FAN_FP_RET_SEND_TO_USERSPACE;
> +}
The sample is a little underwhelming and everyone hates string parsing
in the kernel ;). It'd be nice to see a more real-world use-case for
this.
Could this be used to implement subtree filtering? I guess you'd have
to walk back up the directory tree and see whether it had a given
ancestor?
> +
> +static int add_item(struct sample_fp_data *fp_data, const char *prev)
> +{
> + struct prefix_item *item;
> +
> + item = kzalloc(sizeof(*item), GFP_KERNEL);
> + if (!item)
> + return -ENOMEM;
> + item->prefix = prev;
> + list_add_tail(&item->list, &fp_data->item_list);
> + return 0;
> +}
> +
> +static void free_sample_fp_data(struct sample_fp_data *fp_data)
> +{
> + struct prefix_item *item, *tmp;
> +
> + list_for_each_entry_safe(item, tmp, &fp_data->item_list, list) {
> + list_del_init(&item->list);
> + kfree(item);
> + }
> + kfree(fp_data->str_table);
> + kfree(fp_data);
> +}
> +
> +static int sample_fp_init(struct fanotify_fastpath_hook *fp_hook, const char *args)
> +{
> + struct sample_fp_data *fp_data = kzalloc(sizeof(struct sample_fp_data), GFP_KERNEL);
> + char *p, *prev;
> + int ret;
> +
> + if (!fp_data)
> + return -ENOMEM;
> +
> + /* Make a copy of the list of prefix to ignore */
> + fp_data->str_table = kstrndup(args, FAN_FP_ARGS_MAX, GFP_KERNEL);
> + if (!fp_data->str_table) {
> + ret = -ENOMEM;
> + goto err_out;
> + }
> +
> + INIT_LIST_HEAD(&fp_data->item_list);
> + prev = fp_data->str_table;
> + p = fp_data->str_table;
> +
> + /* Update the list replace ',' with '\n'*/
> + while ((p = strchr(p, ',')) != NULL) {
> + *p = '\0';
> + ret = add_item(fp_data, prev);
> + if (ret)
> + goto err_out;
> + p = p + 1;
> + prev = p;
> + }
> +
> + ret = add_item(fp_data, prev);
> + if (ret)
> + goto err_out;
> +
> + fp_hook->data = fp_data;
> +
> + return 0;
> +
> +err_out:
> + free_sample_fp_data(fp_data);
> + return ret;
> +}
> +
> +static void sample_fp_free(struct fanotify_fastpath_hook *fp_hook)
> +{
> + free_sample_fp_data(fp_hook->data);
> +}
> +
> +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 = "ignore-prefix",
> + .owner = THIS_MODULE,
> +};
> +
> +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..f301c4e0d21a
> --- /dev/null
> +++ b/samples/fanotify/fastpath-user.c
> @@ -0,0 +1,90 @@
> +// 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 = "ignore-prefix",
> + .version = 1,
> + .flags = 0,
> + };
> + char buffer[BUFSIZ];
> + int fd;
> +
> + if (argc < 3) {
> + printf("Usage\n"
> + "\t %s <path to monitor> <prefix to ignore>\n",
> + argv[0]);
> + return 1;
> + }
> +
> + args.init_args = (__u64)argv[2];
> + args.init_args_len = strlen(argv[2]) + 1;
> +
> + fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_NAME | FAN_REPORT_DIR_FID, O_RDONLY);
> + if (fd < 0)
> + errx(1, "fanotify_init");
> +
> + if (fanotify_mark(fd, FAN_MARK_ADD,
> + FAN_OPEN | FAN_ONDIR | FAN_EVENT_ON_CHILD,
> + AT_FDCWD, argv[1])) {
> + errx(1, "fanotify_mark");
> + }
> +
> + if (ioctl(fd, FAN_IOC_ADD_FP, &args))
> + errx(1, "ioctl");
> +
> + while (total_event_cnt < 10) {
> + int n = read(fd, buffer, BUFSIZ);
> +
> + if (n < 0)
> + errx(1, "read");
> +
> + handle_notifications(buffer, n);
> + }
> +
> + ioctl(fd, FAN_IOC_DEL_FP);
> + close(fd);
> +
> + return 0;
> +}
--
Jeff Layton <jlayton@...nel.org>
Powered by blists - more mailing lists