Fwd: Patch to enable netlink messages when adding and removing inotify watches.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I'm sending this to the mainlist, since the maintainers of inotify, to
whom I've send this first do not react.

It's about sending netlink messages when an inotify has been set or removed.
It's intended use is to enable to make FUSE fs's aware a watch is on an inode.
This patch is not enough to make that work, but is required.
More steps are to make the FUSE fs listen to the netlink
(generic/VFS_INOTIFY), set a backend specific notify watch on the
backend, and report anything back to the kernel when something is
reported there, using new calls recently added to FUSE to notify the
kernel.

Stef Bon

---------- Forwarded message ----------
From: Stef Bon <stefbon@xxxxxxxxx>
Date: 2012/1/1
Subject: Patch to enable netlink messages when adding and removing
inotify watches.
To: John McCutchan <john@xxxxxxxxxxxxxxxxx>


Hi,

I want to apply a patch to the kernel to enable the sending of a
netlink message when setting or removing an inotify watch. My goal is
to make FUSE filesystems notify aware.

Since inotify works in the kernel space, and FUSE filesystems are in
userspace, FUSE fs's do not "know" when a watch has been set or
removed.

I think it's a good thing the fs "knows" about a watch, since it can
then set a backend specific notify watch on the backend, and report
anything back to the kernel when something changes.

The new netlink.c file is almost a copy of the netlink.c file in
fs/quota/netlink.c.
For testing I've got a testprogram also supplied. Compile it with:

gcc -Wall -I/usr/include/libnl3  -lnl-3 -lnl-genl-3 testnetlink.c -o testnetlink

This testprogram just report's anything it receives on the
GENERIC/VFS_INOTIFY group.


Please report any comments,

Stef Bon
Voorburg
the Netherlands
diff --git a/fs/notify/inotify/Kconfig b/fs/notify/inotify/Kconfig
index b981fc0..07f9bfc 100644
--- a/fs/notify/inotify/Kconfig
+++ b/fs/notify/inotify/Kconfig
@@ -15,3 +15,11 @@ config INOTIFY_USER
 	  For more information, see <file:Documentation/filesystems/inotify.txt>
 
 	  If unsure, say Y.
+
+config INOTIFY_USER_NETLINK_INTERFACE
+	bool "Report inotify add/remove watch messages through netlink interface"
+	depends on INOTIFY_USER && NET
+	---help---
+	  If you say Y here, inotify messages (about a watch being set or removed
+	  , not the events!) will be reported through netlink interface. If unsure,
+	  say Y.
diff --git a/fs/notify/inotify/Makefile b/fs/notify/inotify/Makefile
index a380dab..f977eaa 100644
--- a/fs/notify/inotify/Makefile
+++ b/fs/notify/inotify/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_INOTIFY_USER)	+= inotify_fsnotify.o inotify_user.o
+obj-$(CONFIG_INOTIFY_USER_NETLINK_INTERFACE)	+= netlink.o
\ No newline at end of file
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 8445fbc..3f056b9 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -786,6 +786,9 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
 
 	/* create/update an inode mark */
 	ret = inotify_update_watch(group, inode, mask);
+
+	if ( ret>=0 ) inotify_send_add_message(fd, ret, mask, pathname);
+
 	path_put(&path);
 fput_and_out:
 	fput_light(filp, fput_needed);
@@ -822,6 +825,8 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd)
 	/* match ref taken by inotify_idr_find */
 	fsnotify_put_mark(&i_mark->fsn_mark);
 
+	inotify_send_remove_message(fd, wd);
+
 out:
 	fput_light(filp, fput_needed);
 	return ret;
diff --git a/fs/notify/inotify/netlink.c b/fs/notify/inotify/netlink.c
new file mode 100644
index 0000000..b965c7f
--- /dev/null
+++ b/fs/notify/inotify/netlink.c
@@ -0,0 +1,151 @@
+
+#include <linux/cred.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/inotify.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+
+/* Netlink family structure for inotify */
+static struct genl_family inotify_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = 0,
+	.name = "VFS_INOTIFY",
+	.version = 1,
+	.maxattr = INOTIFY_NL_A_MAX,
+};
+
+/**
+ * inotify_send_add_message
+ * @fd: inotify fd
+ * @wd: watch descriptor
+ * @mask: the mask
+ * @path: path
+ *
+ */
+
+void inotify_send_add_message(u32 fd, u32 wd, u32 mask, const char *path)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret;
+	int msg_size = 4 * nla_total_size(sizeof(u32)) + nla_total_size(strlen(path)) + 1;
+
+	skb = genlmsg_new(msg_size, GFP_KERNEL);
+
+	if (!skb) {
+
+		printk(KERN_ERR "VFS: Not enough memory to compose inotify add wd netlink message.\n");
+		return;
+
+	}
+
+	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+			&inotify_genl_family, 0, INOTIFY_NL_C_ADD_MESSAGE);
+
+	if (!msg_head) {
+
+		printk(KERN_ERR "VFS: Cannot store netlink header in inotify add wd netlink message.\n");
+		goto err_out;
+
+	}
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_PID, current->pid);
+	if (ret) goto attr_err_out;
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_FD, fd);
+	if (ret) goto attr_err_out;
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_WD, wd);
+	if (ret) goto attr_err_out;
+
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_MASK, mask);
+	if (ret) goto attr_err_out;
+
+	ret = nla_put_string(skb, INOTIFY_NL_A_PATH, path);
+	if (ret) goto attr_err_out;
+
+	genlmsg_end(skb, msg_head);
+
+	genlmsg_multicast(skb, 0, inotify_genl_family.id, GFP_KERNEL);
+
+	return;
+attr_err_out:
+	printk(KERN_ERR "VFS: Error when writing attributes to inotify add wd netlink message!\n");
+err_out:
+	kfree_skb(skb);
+}
+EXPORT_SYMBOL(inotify_send_add_message);
+
+/**
+ * inotify_send_remove_message
+ * @fd: inotify fd
+ * @wd: watch descriptor
+ *
+ */
+
+void inotify_send_remove_message(u32 fd, u32 wd)
+{
+	static atomic_t seq;
+	struct sk_buff *skb;
+	void *msg_head;
+	int ret;
+	int msg_size = 3 * nla_total_size(sizeof(u32));
+
+	skb = genlmsg_new(msg_size, GFP_KERNEL);
+
+	if (!skb) {
+
+		printk(KERN_ERR "VFS: Not enough memory to send inotify remove wd netlink message.\n");
+		return;
+
+	}
+
+	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+			&inotify_genl_family, 0, INOTIFY_NL_C_REMOVE_MESSAGE);
+
+	if (!msg_head) {
+
+		printk(KERN_ERR "VFS: Cannot store netlink header in inotify remove wd netlink message.\n");
+		goto err_out;
+
+	}
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_PID, current->pid);
+	if (ret) goto attr_err_out;
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_FD, fd);
+	if (ret) goto attr_err_out;
+
+	ret = nla_put_u32(skb, INOTIFY_NL_A_WD, wd);
+	if (ret) goto attr_err_out;
+
+	genlmsg_end(skb, msg_head);
+
+	genlmsg_multicast(skb, 0, inotify_genl_family.id, GFP_KERNEL);
+
+	return;
+attr_err_out:
+	printk(KERN_ERR "VFS: Error when writing attributes to inotify remove wd netlink message\n");
+err_out:
+	kfree_skb(skb);
+}
+EXPORT_SYMBOL(inotify_send_remove_message);
+
+
+
+
+static int __init inotify_init(void)
+{
+	if (genl_register_family(&inotify_genl_family) != 0)
+		printk(KERN_ERR"VFS: Failed to create inotify_user netlink interface.\n");
+	return 0;
+};
+
+module_init(inotify_init);
diff --git a/include/linux/inotify.h b/include/linux/inotify.h
index d33041e..4ad0b4f 100644
--- a/include/linux/inotify.h
+++ b/include/linux/inotify.h
@@ -84,4 +84,44 @@ extern struct ctl_table inotify_table[]; /* for sysctl */
 
 #endif
 
+/* commands */
+
+enum {
+	INOTIFY_NL_C_UNSPEC,
+	INOTIFY_NL_C_ADD_MESSAGE,
+	INOTIFY_NL_C_REMOVE_MESSAGE,
+	__INOTIFY_NL_C_MAX,
+};
+
+#define INOTIFY_NL_C_MAX (__INOTIFY_NL_C_MAX - 1)
+
+/* attributes */
+
+enum {
+	INOTIFY_NL_A_UNSPEC,
+	INOTIFY_NL_A_PID,
+	INOTIFY_NL_A_FD,
+	INOTIFY_NL_A_WD,
+	INOTIFY_NL_A_MASK,
+	INOTIFY_NL_A_PATH,
+	__INOTIFY_NL_A_MAX,
+};
+
+#define INOTIFY_NL_A_MAX (__INOTIFY_NL_A_MAX - 1)
+
+
+#ifdef CONFIG_INOTIFY_USER_NETLINK_INTERFACE
+extern void inotify_send_add_message(u32 fd, u32 wd, u32 mask, const char *path);
+extern void inotify_send_remove_message(u32 fd, u32 wd);
+#else
+static inline void inotify_send_add_message(u32 fd, u32 wd, u32 mask, const char *path)
+{
+	return;
+}
+static inline void inotify_send_remove_message(u32 fd, u32 wd);
+{
+	return;
+}
+#endif /* CONFIG_INOTIFY_USER_NETLINK_INTERFACE */
+
 #endif	/* _LINUX_INOTIFY_H */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <linux/types.h>
#include <linux/netlink.h>

#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>

#include <linux/inotify.h>


#define MAX_PAYLOAD 4100

/* commands */

enum {
	INOTIFY_NL_C_UNSPEC,
	INOTIFY_NL_C_ADD_MESSAGE,
	INOTIFY_NL_C_REMOVE_MESSAGE,
	__INOTIFY_NL_C_MAX,
};

#define INOTIFY_NL_C_MAX (__INOTIFY_NL_C_MAX - 1)

/* attributes */

enum {
	INOTIFY_NL_A_UNSPEC,
	INOTIFY_NL_A_PID,
	INOTIFY_NL_A_FD,
	INOTIFY_NL_A_WD,
	INOTIFY_NL_A_MASK,
	INOTIFY_NL_A_PATH,
	__INOTIFY_NL_A_MAX,
};


#define INOTIFY_NL_A_MAX (__INOTIFY_NL_A_MAX - 1)

static struct nla_policy inotify_nl_message_policy[INOTIFY_NL_A_MAX+1] = {
	[INOTIFY_NL_A_PID] = { .type = NLA_U32 },
	[INOTIFY_NL_A_FD] = { .type = NLA_U32 },
	[INOTIFY_NL_A_WD] = { .type = NLA_U32 },
	[INOTIFY_NL_A_MASK] = { .type = NLA_U32 },
	[INOTIFY_NL_A_PATH] = { .type = NLA_STRING },
};

static void print_mask(unsigned int mask)
{

    if ( mask & IN_ACCESS ) fprintf(stderr, "IN_ACCESS\n");
    if ( mask & IN_MODIFY ) fprintf(stderr, "IN_MODIFY\n");
    if ( mask & IN_ATTRIB ) fprintf(stderr, "IN_ATTRIB\n");
    if ( mask & IN_CLOSE_WRITE ) fprintf(stderr, "IN_CLOSE_WRITE\n");
    if ( mask & IN_CLOSE_NOWRITE ) fprintf(stderr, "IN_CLOSE_NOWRITE\n");
    if ( mask & IN_OPEN ) fprintf(stderr, "IN_OPEN\n");
    if ( mask & IN_MOVED_FROM ) fprintf(stderr, "IN_MOVED_FROM\n");
    if ( mask & IN_MOVED_TO ) fprintf(stderr, "IN_MOVED_TO\n");
    if ( mask & IN_CREATE ) fprintf(stderr, "IN_CREATE\n");
    if ( mask & IN_DELETE ) fprintf(stderr, "IN_DELETE\n");
    if ( mask & IN_DELETE_SELF ) fprintf(stderr, "IN_DELETE_SELF\n");
    if ( mask & IN_MOVE_SELF ) fprintf(stderr, "IN_MOVE_SELF\n");

    if ( mask & IN_ONLYDIR ) fprintf(stderr, "IN_ONLYDIR\n");
    if ( mask & IN_DONT_FOLLOW ) fprintf(stderr, "IN_DONT_FOLLOW\n");
    if ( mask & IN_EXCL_UNLINK ) fprintf(stderr, "IN_EXCL_UNLINK\n");
    if ( mask & IN_MASK_ADD ) fprintf(stderr, "IN_MASK_ADD\n");
    if ( mask & IN_ISDIR ) fprintf(stderr, "IN_ISDIR\n");
    if ( mask & IN_ONESHOT ) fprintf(stderr, "IN_ONESHOT\n");

}



/* Parse netlink message and process it. */
static int inotify_nl_parser_payload(struct nlmsghdr *nlh)
{
	struct genlmsghdr *ghdr;
	struct nlattr *attrs[INOTIFY_NL_A_MAX+1];
	int ret;

	if (!genlmsg_valid_hdr(nlh, 0)) {

            fprintf(stderr, "header not valid.\n");
            return 0;

        }

        ghdr = nlmsg_data(nlh);

	/* Unknown message? Ignore... */
	if (ghdr->cmd != INOTIFY_NL_C_ADD_MESSAGE && ghdr->cmd != INOTIFY_NL_C_REMOVE_MESSAGE) {

            fprintf(stderr, "message not reckognized.\n");
            return 0;

        }

        ret = genlmsg_parse(nlh, 0, attrs, INOTIFY_NL_A_MAX, inotify_nl_message_policy);

        if (ret < 0) {

            fprintf(stderr, "Error parsing netlink message.\n");
            return ret;
	}

	if ( ! attrs[INOTIFY_NL_A_PID] ) {

            fprintf(stderr, "Missing mask in message.\n");

        }

	if ( ! attrs[INOTIFY_NL_A_FD] ) {

            fprintf(stderr, "Missing mask in message.\n");

        }

	if ( ! attrs[INOTIFY_NL_A_WD] ) {

            fprintf(stderr, "Missing mask in message.\n");

        }

        if ( ghdr->cmd == INOTIFY_NL_C_ADD_MESSAGE ) {

	    if ( ! attrs[INOTIFY_NL_A_MASK] ) fprintf(stderr, "Missing mask in message.\n");

            if ( ! attrs[INOTIFY_NL_A_PATH] ) fprintf(stderr, "Missing path in message.\n");

            fprintf(stdout, "inotify_add: pid: %i fd:%i wd: %i mask: %i path: %s\n", 
                nla_get_u32(attrs[INOTIFY_NL_A_PID]), 
                nla_get_u32(attrs[INOTIFY_NL_A_FD]), 
                nla_get_u32(attrs[INOTIFY_NL_A_WD]), 
                nla_get_u32(attrs[INOTIFY_NL_A_MASK]), 
                nla_get_string(attrs[INOTIFY_NL_A_PATH]));

            print_mask(nla_get_u32(attrs[INOTIFY_NL_A_MASK]));


        } else {

            fprintf(stdout, "inotify_remove: pid: %i fd:%i wd: %i\n", 
                nla_get_u32(attrs[INOTIFY_NL_A_PID]), 
                nla_get_u32(attrs[INOTIFY_NL_A_FD]), 
                nla_get_u32(attrs[INOTIFY_NL_A_WD]));

        }

        return 0;
}


int main(int argc, char *argv[])
{
    struct sockaddr_nl nls;
    struct pollfd pfd;
    // struct msghdr msg;
    char buf[4100];
    struct iovec iov = { buf, sizeof(buf) };
    struct nlmsghdr *nlh;
    int len, i;
    struct msghdr msg = {
                .msg_name=(void *) &nls, 
                .msg_namelen=sizeof(nls),
                .msg_iov=&iov, 
                .msg_iovlen=1, 
                .msg_control=NULL, 
                .msg_controllen=0, 
                .msg_flags=0 };


    // netlink socket

    memset(&nls,0,sizeof(struct sockaddr_nl));

    nls.nl_family = AF_NETLINK;
    nls.nl_pid = getpid();
    nls.nl_groups = -1; /* multicast */

    pfd.events = POLLIN;
    pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC);

    if (pfd.fd==-1) {

        fprintf(stderr, "Not root\n");
        exit(1);

    }


    // Listen to netlink socket

    if ( bind(pfd.fd, (void *) &nls, sizeof(struct sockaddr_nl)) ) {

        fprintf(stderr, "Bind failed\n");
        exit(1);

    }



    while (-1!=poll(&pfd, 1, -1)) {



        len = recvmsg(pfd.fd, &msg, 0);

        if (len == -1) {

            fprintf(stderr, "recv\n");

        } else {

            // Parse the message

            fprintf(stdout, "activity....\n");

            for (nlh=(struct nlmsghdr *) buf; NLMSG_OK(nlh, len); nlh=NLMSG_NEXT(nlh, len)) {

                if ( nlh->nlmsg_type==NLMSG_DONE ) {

                    fprintf(stdout, "ready with message\n");

                } else if ( nlh->nlmsg_type==NLMSG_ERROR ) {

                    fprintf(stdout, "error message\n");

                } else {

                    fprintf(stdout, "parsing message\n");

                    i=inotify_nl_parser_payload(nlh);

                }

            }

        }

    }



    // Dear gcc: shut up.
    return 0;

}

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux