Re: [2.6.16 PATCH] Connector: Filesystem Events Connector v3

From: Evgeniy Polyakov
Date: Fri Mar 24 2006 - 03:03:24 EST


On Fri, Mar 24, 2006 at 11:27:56AM +0800, Yi Yang (yang.y.yi@xxxxxxxxx) wrote:
> v3 release has a minimal modification according to Evgeniy and Matt's
> suggestion.
>
> This patch implements a new connector, Filesystem Event Connector,
> the user can monitor filesystem activities via it, currently, it
> can monitor access, attribute change, open, create, modify, delete,
> move and close of any file or directory.
>
> Every filesystem event will include tgid, uid and gid of the process
> which triggered this event, process name, file or directory name
> operated by it.
>
> Filesystem events connector is never a duplicate of inotify, inotify
> just concerns change on file or directory, Beagle uses it to watch
> file changes in order to regenerate index for it, inotify can't tell
> us who did that change and what is its process name, but filesystem
> events connector can do these, moreover inotify's overhead is greater
> than filesystem events connector, inotify needs compare inode with
> watched file or directories list to decide whether it should generate an
> inotify_event, some locks also increase overhead, filesystem event
> connector hasn't these overhead, it just generates a fsevent and send.
>
> It is also never redundant functionality of audit subsystem, if enable
> audit option, audit subsystem will audit all the syscalls, so it adds
> big overhead for the whole system, but Filesystem Event Connector just
> concerns those operations related to the filesystem, so it has little
> overhead, moreover, audit subsystem is a complicated function block,
> it not only sends audit results to netlink interface, but also send
> them to klog or syslog, so it will add big overhead. you can look File
> Event Connector as subset of audit subsystem, but File Event Connector
> is a very very lightweight subsystem.
>
>
> To be important, filesystem event connector doesn't add any new system
> call, the user space application can make use of it by netlink socket,
> but inotify added several system calls, many events mechanism in kernel
> have used netlink as communication way with user space, for example,
> KOBJECT_UEVENT, PROC_EVENTS, to use netlink will make it more possible
> to unify events interface to netlink, the user space application can use
> it very easy.
>
>
> drivers/connector/Kconfig | 9 ++
> drivers/connector/Makefile | 1
> drivers/connector/cn_fs.c | 200
> +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/connector.h | 8 +
> include/linux/fsevent.h | 86 +++++++++++++++++++
> include/linux/fsnotify.h | 37 ++++++++
> 6 files changed, 340 insertions(+), 1 deletion(-)
>
>
> Signed-off-by: Yi Yang <yang.y.yi@xxxxxxxxx>

Ack.

> --- a/include/linux/connector.h.orig 2006-03-23 09:07:32.000000000 +0800
> +++ b/include/linux/connector.h 2006-03-24 10:10:59.000000000 +0800
> @@ -35,7 +35,13 @@
> #define CN_IDX_CIFS 0x2
> #define CN_VAL_CIFS 0x1
>
> -#define CN_NETLINK_USERS 1
> +/*
> + * Filesystem Events Connector unique ids -- used for message routing
> + */
> +#define CN_IDX_FS 0x3
> +#define CN_VAL_FS 0x1
> +
> +#define CN_NETLINK_USERS 3
>
> /*
> * Maximum connector's message size.
> --- a/include/linux/fsnotify.h.orig 2006-03-23 09:07:46.000000000 +0800
> +++ b/include/linux/fsnotify.h 2006-03-23 09:18:29.000000000 +0800
> @@ -15,6 +15,7 @@
>
> #include <linux/dnotify.h>
> #include <linux/inotify.h>
> +#include <linux/fsevent.h>
>
> /*
> * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir
> @@ -45,6 +46,8 @@ static inline void fsnotify_move(struct
> if (source) {
> inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL);
> }
> + raise_fsevent_move(old_dir, old_name, new_dir, new_name,
> + FSEVENT_MOVE | (isdir?FSEVENT_ISDIR:0));
> }
>
> /*
> @@ -56,6 +59,8 @@ static inline void fsnotify_nameremove(s
> isdir = IN_ISDIR;
> dnotify_parent(dentry, DN_DELETE);
> inotify_dentry_parent_queue_event(dentry, IN_DELETE|isdir, 0,
> dentry->d_name.name);
> + raise_fsevent(dentry,
> + FSEVENT_DELETE | (isdir?FSEVENT_ISDIR:0));
> }
>
> /*
> @@ -74,6 +79,7 @@ static inline void fsnotify_create(struc
> {
> inode_dir_notify(inode, DN_CREATE);
> inotify_inode_queue_event(inode, IN_CREATE, 0, name);
> + raise_fsevent_create(inode, name, FSEVENT_CREATE);
> }
>
> /*
> @@ -83,6 +89,8 @@ static inline void fsnotify_mkdir(struct
> {
> inode_dir_notify(inode, DN_CREATE);
> inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, name);
> + raise_fsevent_create(inode, name,
> + FSEVENT_CREATE | FSEVENT_ISDIR);
> }
>
> /*
> @@ -99,6 +107,8 @@ static inline void fsnotify_access(struc
> dnotify_parent(dentry, DN_ACCESS);
> inotify_dentry_parent_queue_event(dentry, mask, 0,
> dentry->d_name.name);
> inotify_inode_queue_event(inode, mask, 0, NULL);
> + raise_fsevent(dentry, FSEVENT_ACCESS |
> + ((S_ISDIR(inode->i_mode))?FSEVENT_ISDIR:0));
> }
>
> /*
> @@ -115,6 +125,8 @@ static inline void fsnotify_modify(struc
> dnotify_parent(dentry, DN_MODIFY);
> inotify_dentry_parent_queue_event(dentry, mask, 0,
> dentry->d_name.name);
> inotify_inode_queue_event(inode, mask, 0, NULL);
> + raise_fsevent(dentry, FSEVENT_MODIFY |
> + ((S_ISDIR(inode->i_mode))?FSEVENT_ISDIR:0));
> }
>
> /*
> @@ -130,6 +142,9 @@ static inline void fsnotify_open(struct
>
> inotify_dentry_parent_queue_event(dentry, mask, 0,
> dentry->d_name.name);
> inotify_inode_queue_event(inode, mask, 0, NULL);
> + raise_fsevent(dentry, FSEVENT_OPEN |
> + ((S_ISDIR(inode->i_mode))?FSEVENT_ISDIR:0));
> +
> }
>
> /*
> @@ -148,6 +163,8 @@ static inline void fsnotify_close(struct
>
> inotify_dentry_parent_queue_event(dentry, mask, 0, name);
> inotify_inode_queue_event(inode, mask, 0, NULL);
> + raise_fsevent(dentry, FSEVENT_CLOSE |
> + ((S_ISDIR(inode->i_mode))?FSEVENT_ISDIR:0));
> }
>
> /*
> @@ -163,6 +180,8 @@ static inline void fsnotify_xattr(struct
>
> inotify_dentry_parent_queue_event(dentry, mask, 0,
> dentry->d_name.name);
> inotify_inode_queue_event(inode, mask, 0, NULL);
> + raise_fsevent(dentry, FSEVENT_MODIFY_ATTRIB |
> + ((S_ISDIR(inode->i_mode))?FSEVENT_ISDIR:0));
> }
>
> /*
> @@ -213,6 +232,24 @@ static inline void fsnotify_change(struc
> inotify_dentry_parent_queue_event(dentry, in_mask, 0,
> dentry->d_name.name);
> }
> +
> +#ifdef CONFIG_FS_EVENTS
> + {
> + u32 fsevent_mask = 0;
> + if (ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE))
> + fsevent_mask |= FSEVENT_MODIFY_ATTRIB;
> + if ((ia_valid & ATTR_ATIME) && (ia_valid & ATTR_MTIME))
> + fsevent_mask |= FSEVENT_MODIFY_ATTRIB;
> + else if (ia_valid & ATTR_ATIME)
> + fsevent_mask |= FSEVENT_ACCESS;
> + else if (ia_valid & ATTR_MTIME)
> + fsevent_mask |= FSEVENT_MODIFY;
> + if (ia_valid & ATTR_SIZE)
> + fsevent_mask |= FSEVENT_MODIFY;
> + if (fsevent_mask)
> + raise_fsevent(dentry, fsevent_mask);
> + }
> +#endif /* CONFIG_FS_EVENTS */
> }
>
> #ifdef CONFIG_INOTIFY /* inotify helpers */
> --- a/drivers/connector/Makefile.orig 2006-03-23 09:06:54.000000000 +0800
> +++ b/drivers/connector/Makefile 2006-03-23 09:18:29.000000000 +0800
> @@ -1,4 +1,5 @@
> obj-$(CONFIG_CONNECTOR) += cn.o
> obj-$(CONFIG_PROC_EVENTS) += cn_proc.o
> +obj-$(CONFIG_FS_EVENTS) += cn_fs.o
>
> cn-y += cn_queue.o connector.o
> --- a/drivers/connector/Kconfig.orig 2006-03-23 09:06:37.000000000 +0800
> +++ b/drivers/connector/Kconfig 2006-03-24 11:00:21.000000000 +0800
> @@ -18,4 +18,13 @@ config PROC_EVENTS
> Provide a connector that reports process events to userspace. Send
> events such as fork, exec, id change (uid, gid, suid, etc), and
> exit.
>
> +config FS_EVENTS
> + boolean "Report filesystem events to userspace"
> + depends on CONNECTOR=y
> + default y
> + ---help---
> + Provide a connector that reports filesystem events to userspace.
> The
> + reported event include access, write, utime, chmod, chown, chgrp,
> + close, open, create, rename, unlink, mkdir, rmdir.
> +
> endmenu
> --- /dev/null 2006-03-07 18:01:03.020977648 +0800
> +++ b/include/linux/fsevent.h 2006-03-24 09:57:38.000000000 +0800
> @@ -0,0 +1,86 @@
> +/*
> + * fsevent.h - filesystem events connector
> + *
> + * Copyright (C) 2006 Yi Yang <yang.y.yi@xxxxxxxxx>
> + * Based on cn_proc.h by Matt Helsley, IBM Corp
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#ifndef LINUX_FSEVENT_H
> +#define LINUX_FSEVENT_H
> +
> +#include <linux/types.h>
> +#include <linux/time.h>
> +#include <linux/connector.h>
> +
> +enum fsevent_type {
> + FSEVENT_ACCESS = 0x00000001, /* File was accessed */
> + FSEVENT_MODIFY = 0x00000002, /* File was modified */
> + FSEVENT_MODIFY_ATTRIB = 0x00000004, /* Metadata changed */
> + FSEVENT_CLOSE = 0x00000008, /* File was closed */
> + FSEVENT_OPEN = 0x00000010, /* File was opened */
> + FSEVENT_MOVE = 0x00000020, /* File was moved */
> + FSEVENT_CREATE = 0x00000040, /* File was created */
> + FSEVENT_DELETE = 0x00000080, /* File was deleted */
> + FSEVENT_CMDACK = 0x40000000, /* For use internally */
> + FSEVENT_ISDIR = 0x80000000 /* It is set for a dir */
> +};
> +
> +/*
> + * Userspace sends this enum to register with the kernel that it is
> listening
> + * for events on the connector.
> + */
> +enum fsevent_mode {
> + FSEVENT_LISTEN = 1,
> + FSEVENT_IGNORE = 2
> +};
> +
> +struct fsevent {
> + __u32 type;
> + __u32 cpu;
> + struct timespec timestamp;
> + pid_t tgid;
> + uid_t uid;
> + gid_t gid;
> + int err;
> + __u32 len;
> + __u32 pname_len;
> + __u32 fname_len;
> + __u32 new_fname_len;
> + char name[0];
> +};
> +
> +#ifdef __KERNEL__
> +#ifdef CONFIG_FS_EVENTS
> +extern void raise_fsevent(struct dentry * dentryp, u32 mask);
> +extern void raise_fsevent_move(struct inode * olddir, const char *
> oldname, + struct inode * newdir, const char * newname, u32
> mask);
> +extern void raise_fsevent_create(struct inode * inode,
> + const char * name, u32 mask);
> +#else
> +static void raise_fsevent(struct dentry * dentryp, u32 mask)
> +{}
> +
> +static void raise_fsevent_move(struct inode * olddir, const char *
> oldname, + struct inode * newdir, const char * newname, u32
> mask)
> +{}
> +
> +static void raise_fsevent_create(struct inode * inode,
> + const char * name, u32 mask)
> +{}
> +#endif /* CONFIG_FS_EVENTS */
> +#endif /* __KERNEL__ */
> +#endif /* LINUX_FSEVENT_H */
> --- /dev/null 2006-03-07 18:01:03.020977648 +0800
> +++ b/drivers/connector/cn_fs.c 2006-03-24 10:34:11.000000000 +0800
> @@ -0,0 +1,200 @@
> +/*
> + * cn_fs.c - filesystem events connector
> + *
> + * Copyright (C) 2006 Yi Yang <yang.y.yi@xxxxxxxxx>
> + * Based on cn_proc.c by Matt Helsley, IBM Corp
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/ktime.h>
> +#include <linux/init.h>
> +#include <linux/fs.h>
> +#include <linux/fsevent.h>
> +#include <asm/atomic.h>
> +
> +#define CN_FS_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct fsevent))
> +
> +static atomic_t cn_fs_event_listeners = ATOMIC_INIT(0);
> +static struct cb_id cn_fs_event_id = { CN_IDX_FS, CN_VAL_FS };
> +
> +static DEFINE_PER_CPU(__u32, cn_fs_event_counts) = { 0 };
> +static int fsevent_burst_limit = 100;
> +static int fsevent_ratelimit = 5 * HZ;
> +static unsigned long last = 0;
> +static int fsevent_sum = 0;
> +
> +static inline void get_seq(__u32 *ts, int *cpu)
> +{
> + *ts = get_cpu_var(cn_fs_event_counts)++;
> + *cpu = smp_processor_id();
> + put_cpu_var(cn_fs_event_counts);
> +}
> +
> +static void append_string(char **dest, const char *src, size_t len)
> +{
> + strncpy(*dest, src, len);
> + (*dest)[len] = '\0';
> + *dest += len + 1;
> +}
> +
> +int __raise_fsevent(const char * oldname, const char * newname, u32 mask)
> +{
> + struct cn_msg *msg;
> + struct fsevent *event;
> + char * buffer;
> + int namelen = 0;
> + char * nameptr = NULL;
> +
> + if (atomic_read(&cn_fs_event_listeners) < 1)
> + return 0;
> +
> + if (jiffies - last <= fsevent_ratelimit) {
> + if (fsevent_sum > fsevent_burst_limit)
> + return -2;
> + fsevent_sum++;
> + } else {
> + last = jiffies;
> + fsevent_sum = 0;
> + }
> +
> + namelen = strlen(current->comm) + strlen(oldname) + 2;
> + if (newname)
> + namelen += strlen(newname) + 1;
> +
> + buffer = kmalloc(CN_FS_MSG_SIZE + namelen, GFP_KERNEL);
> + if (buffer == NULL)
> + return -1;
> +
> + msg = (struct cn_msg*)buffer;
> + event = (struct fsevent*)msg->data;
> + get_seq(&msg->seq, &event->cpu);
> + ktime_get_ts(&event->timestamp);
> + event->type = mask;
> + event->tgid = current->tgid;
> + event->uid = current->uid;
> + event->gid = current->gid;
> + nameptr = event->name;
> + event->pname_len = strlen(current->comm);
> + append_string(&nameptr, current->comm, event->pname_len);
> + event->fname_len = strlen(oldname);
> + append_string(&nameptr, oldname, event->fname_len);
> + event->len = event->pname_len + event->fname_len + 2;
> + event->new_fname_len = 0;
> + if (newname) {
> + event->new_fname_len = strlen(newname);
> + append_string(&nameptr, newname, event->new_fname_len);
> + event->len += event->new_fname_len + 1;
> + }
> +
> + memcpy(&msg->id, &cn_fs_event_id, sizeof(msg->id));
> + msg->ack = 0;
> + msg->len = sizeof(struct fsevent) + event->len;
> + cn_netlink_send(msg, CN_IDX_FS, GFP_KERNEL);
> + kfree(buffer);
> + return 0;
> +}
> +
> +void raise_fsevent(struct dentry * dentryp, u32 mask)
> +{
> + __raise_fsevent(dentryp->d_name.name, NULL, mask);
> +}
> +EXPORT_SYMBOL_GPL(raise_fsevent);
> +
> +void raise_fsevent_create(struct inode * inode, const char * name, u32
> mask)
> +{
> + __raise_fsevent(name, NULL, mask);
> +}
> +EXPORT_SYMBOL_GPL(raise_fsevent_create);
> +
> +void raise_fsevent_move(struct inode * olddir, const char * oldname,
> + struct inode * newdir, const char * newname, u32 mask)
> +{
> + __raise_fsevent(oldname, newname, mask);
> +}
> +EXPORT_SYMBOL_GPL(raise_fsevent_move);
> +
> +static void cn_fs_event_ack(int err, int rcvd_seq, int rcvd_ack)
> +{
> + struct cn_msg *msg;
> + struct fsevent *event;
> + char * buffer = NULL;
> +
> + if (atomic_read(&cn_fs_event_listeners) < 1)
> + return;
> +
> + buffer = kmalloc(CN_FS_MSG_SIZE, GFP_KERNEL);
> + if (buffer == NULL)
> + return;
> +
> + msg = (struct cn_msg*)buffer;
> + event = (struct fsevent*)msg->data;
> + msg->seq = rcvd_seq;
> + ktime_get_ts(&event->timestamp);
> + event->cpu = -1;
> + event->type = FSEVENT_CMDACK;
> + event->tgid = 0;
> + event->uid = 0;
> + event->gid = 0;
> + event->len = 0;
> + event->pname_len = 0;
> + event->fname_len = 0;
> + event->new_fname_len = 0;
> + event->err = err;
> + memcpy(&msg->id, &cn_fs_event_id, sizeof(msg->id));
> + msg->ack = rcvd_ack + 1;
> + msg->len = sizeof(struct fsevent);
> + cn_netlink_send(msg, CN_IDX_FS, GFP_KERNEL);
> +}
> +
> +static void cn_fsevent_mode_ctl(void *data)
> +{
> + struct cn_msg *msg = data;
> + enum fsevent_mode *mode = NULL;
> + int err = 0;
> +
> + if (msg->len != sizeof(*mode))
> + return;
> +
> + mode = (enum fsevent_mode*)msg->data;
> + switch (*mode) {
> + case FSEVENT_LISTEN:
> + atomic_inc(&cn_fs_event_listeners);
> + break;
> + case FSEVENT_IGNORE:
> + atomic_dec(&cn_fs_event_listeners);
> + break;
> + default:
> + err = -EINVAL;
> + break;
> + }
> + cn_fs_event_ack(err, msg->seq, msg->ack);
> +}
> +
> +static int __init cn_fs_init(void)
> +{
> + int err;
> +
> + if ((err = cn_add_callback(&cn_fs_event_id, "cn_fs",
> + &cn_fsevent_mode_ctl))) {
> + printk(KERN_WARNING "cn_fs failed to register\n");
> + return err;
> + }
> + return 0;
> +}
> +
> +module_init(cn_fs_init);
>

--
Evgeniy Polyakov
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/