[PATCH -v2 06/16] fanotify: add a userspace interface for fanotifynotifications

From: Eric Paris
Date: Tue Oct 14 2008 - 16:55:48 EST


notifications from fanotify need a userspace interface. We are going to
try to do it with sockets. Scary....

Signed-off-by: Eric Paris <eparis@xxxxxxxxxx>
---

include/linux/fanotify.h | 18 +++
include/linux/socket.h | 5 +
net/Makefile | 1
net/core/sock.c | 6 +
net/fanotify/Makefile | 5 +
net/fanotify/af_fanotify.c | 232 ++++++++++++++++++++++++++++++++++++++++++++
net/fanotify/af_fanotify.h | 20 ++++
7 files changed, 283 insertions(+), 4 deletions(-)
create mode 100644 net/fanotify/Makefile
create mode 100644 net/fanotify/af_fanotify.c
create mode 100644 net/fanotify/af_fanotify.h

diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index c991bd9..5add54f 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -41,11 +41,22 @@
#include <sys/types.h>
#endif

+struct fanotify_addr {
+ uint32_t group_num;
+ uint32_t mask;
+ uint32_t timeout;
+ uint32_t unused[9];
+};
+
+/* struct used for FANOTIFY_GET_EVENT */
struct fanotify_event_metadata {
int32_t fd;
uint32_t mask;
};

+/* fanotify getsockopt optvals */
+#define FANOTIFY_GET_EVENT 1
+
#ifdef __KERNEL__

#include <linux/fs.h>
@@ -74,6 +85,8 @@ extern void fanotify(struct file *file, unsigned int mask);
extern void fanotify_get_group(struct fanotify_group *group);
extern struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask);
extern void fanotify_put_group(struct fanotify_group *group);
+
+extern int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock);
#else

static inline void fanotify(struct file *file, unsigned int mask)
@@ -90,6 +103,11 @@ static inline struct fanotify_group *fanotify_find_group(unsigned int group_num,
static inline extern void fanotify_put_group(struct fanotify_group *group)
{}

+static inline int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock)
+{
+ memset(data, 0, sizeof(struct fanotify_event_metadata));
+ return 0;
+}
#endif /* CONFIG_FANOTIFY */

#endif /* __KERNEL __ */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 20fc4bb..4fe7b83 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -191,7 +191,8 @@ struct ucred {
#define AF_RXRPC 33 /* RxRPC sockets */
#define AF_ISDN 34 /* mISDN sockets */
#define AF_PHONET 35 /* Phonet sockets */
-#define AF_MAX 36 /* For now.. */
+#define AF_FANOTIFY 36 /* fscking all access sockets */
+#define AF_MAX 37 /* For now.. */

/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -229,6 +230,7 @@ struct ucred {
#define PF_RXRPC AF_RXRPC
#define PF_ISDN AF_ISDN
#define PF_PHONET AF_PHONET
+#define PF_FANOTIFY AF_FANOTIFY
#define PF_MAX AF_MAX

/* Maximum queue length specifiable by listen. */
@@ -298,6 +300,7 @@ struct ucred {
#define SOL_PPPOL2TP 273
#define SOL_BLUETOOTH 274
#define SOL_PNPIPE 275
+#define SOL_FANOTIFY 276

/* IPX options */
#define IPX_TYPE 1
diff --git a/net/Makefile b/net/Makefile
index 27d1f10..5b98ba4 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_DECNET) += decnet/
obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_PHONET) += phonet/
+obj-$(CONFIG_FANOTIFY) += fanotify/
ifneq ($(CONFIG_VLAN_8021Q),)
obj-y += 8021q/
endif
diff --git a/net/core/sock.c b/net/core/sock.c
index 5e2a313..c7b0cee 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -155,7 +155,7 @@ static const char *af_family_key_strings[AF_MAX+1] = {
"sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
- "sk_lock-AF_MAX"
+ "sk_lock-AF_FANOTIFY", "sk_lock-AF_MAX"
};
static const char *af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
@@ -170,7 +170,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
"slock-27" , "slock-28" , "slock-AF_CAN" ,
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
- "slock-AF_MAX"
+ "slock=AF_FANOTIFY", "slock-AF_MAX"
};
static const char *af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
@@ -185,7 +185,7 @@ static const char *af_family_clock_key_strings[AF_MAX+1] = {
"clock-27" , "clock-28" , "clock-AF_CAN" ,
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
- "clock-AF_MAX"
+ "clock-AF_FANOTIFY", "clock-AF_MAX"
};
#endif

diff --git a/net/fanotify/Makefile b/net/fanotify/Makefile
new file mode 100644
index 0000000..348cf5e
--- /dev/null
+++ b/net/fanotify/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the fanotify AF.
+#
+
+obj-$(CONFIG_FANOTIFY) += af_fanotify.o
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
new file mode 100644
index 0000000..eb2d023
--- /dev/null
+++ b/net/fanotify/af_fanotify.c
@@ -0,0 +1,232 @@
+#include <linux/errno.h>
+#include <linux/fanotify.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/poll.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_fanotify.h"
+
+static const struct proto_ops fanotify_proto_ops;
+
+static struct proto fanotify_proto = {
+ .name = "FANOTIFY",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct fanotify_sock),
+};
+
+static int fan_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct fanotify_sock *fan_sock;
+
+ /* FIXME maybe a new LSM hook? */
+ if (!capable(CAP_NET_RAW))
+ return -EPERM;
+
+ if (protocol != 0)
+ return -ESOCKTNOSUPPORT;
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ sock->state = SS_UNCONNECTED;
+
+ sk = sk_alloc(net, PF_FANOTIFY, GFP_KERNEL, &fanotify_proto);
+ if (sk == NULL)
+ return -ENOBUFS;
+
+ sock->ops = &fanotify_proto_ops;
+
+ sock_init_data(sock, sk);
+
+ sk->sk_family = PF_FANOTIFY;
+ //sk->sk_destruct = packet_sock_destruct;
+ sk_refcnt_debug_inc(sk);
+
+ fan_sock = fan_sk(sk);
+ fan_sock->group = NULL;
+
+ return 0;
+}
+
+static int fan_release(struct socket *sock)
+{
+ struct sock *sk;
+ struct fanotify_sock *fan_sock;
+
+ struct files_struct *files;
+ struct socket *testsock;
+ struct fdtable *fdt;
+ long j = -1;
+ unsigned long set, i;
+ int found = 0, err;
+
+ sk = sock->sk;
+ if (!sk)
+ return 0;
+
+ fan_sock = fan_sk(sk);
+
+ if (sock->state == SS_CONNECTED) {
+ sock->state = SS_UNCONNECTED;
+ fanotify_put_group(fan_sock->group);
+ }
+
+ fan_sock->group = NULL;
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ /* Purge queues */
+
+ sk_refcnt_debug_release(sk);
+
+ sock_put(sk);
+
+ /*
+ * we do all of this shit to figure out if this process is closing the last
+ * fanotify socket. If this is the last one being closed we clear the
+ * exclusion flag. If this is not the last close we should remain excluded.
+ *
+ * the basic idea behind running all of the open fd's was stolen from
+ * fs/exec.c:flush_old_files.
+ */
+ files = current->files;
+ if (files)
+ spin_lock(&files->file_lock);
+ for (; !found && files ;) {
+ j++;
+ i = j * __NFDBITS;
+ fdt = files_fdtable(files);
+ if (i >= fdt->max_fds)
+ break;
+ set = fdt->open_fds->fds_bits[j];
+ if (!set)
+ continue;
+ spin_unlock(&files->file_lock);
+ for ( ; set && !found ; i++, set >>= 1) {
+ if (set & 1) {
+ testsock = sockfd_lookup(i, &err);
+ if (!testsock)
+ continue;
+ if ((testsock != sock) && (testsock->ops == &fanotify_proto_ops))
+ found = 1;
+ sockfd_put(testsock);
+ }
+ }
+ spin_lock(&files->file_lock);
+ }
+ if (files)
+ spin_unlock(&files->file_lock);
+
+ if (!found)
+ current->flags &= ~PF_NOFACCESS;
+
+ return 0;
+}
+
+static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+ struct fanotify_addr *fan_addr = (struct fanotify_addr *)addr;
+ struct fanotify_sock *fan_sock;
+
+ if (addr_len != sizeof(struct fanotify_addr))
+ return -EINVAL;
+
+ fan_sock = fan_sk(sock->sk);
+ fan_sock->group = fanotify_find_group(fan_addr->group_num, fan_addr->mask);
+
+ if (IS_ERR(fan_sock->group))
+ return PTR_ERR(fan_sock->group);
+
+ sock->state = SS_CONNECTED;
+ current->flags |= PF_NOFACCESS;
+
+ return 0;
+}
+
+static unsigned int fan_poll(struct file * file, struct socket *sock, poll_table *pt)
+{
+ return 0;
+}
+
+static int fan_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+{
+ struct fanotify_sock *fan_sock;
+ struct fanotify_group *group;
+ struct fanotify_event_metadata metadata;
+ int ret = 0;
+
+ if (sock->state != SS_CONNECTED)
+ return -EBADF;
+
+ fan_sock = fan_sk(sock->sk);
+ group = fan_sock->group;
+
+ switch (optname) {
+ case FANOTIFY_GET_EVENT:
+ if (*optlen < sizeof(struct fanotify_event_metadata))
+ return -ENOMEM;
+ else
+ *optlen = sizeof(struct fanotify_event_metadata);
+ ret = fanotify_create_event_fd(group, &metadata, !!(sock->file->f_flags & O_NONBLOCK));
+ if (ret)
+ break;
+
+ ret = copy_to_user(optval, &metadata, sizeof(struct fanotify_event_metadata));
+ break;
+ default:
+ return -ENOPROTOOPT;
+ };
+
+ return ret;
+}
+
+static const struct net_proto_family fanotify_family_ops = {
+ .family = PF_FANOTIFY,
+ .create = fan_sock_create,
+ .owner = THIS_MODULE,
+};
+
+static const struct proto_ops fanotify_proto_ops = {
+ .family = PF_FANOTIFY,
+ .owner = THIS_MODULE,
+ .release = fan_release,
+ .bind = fan_bind,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = fan_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = fan_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int __init fanotify_socket_register(void)
+{
+ int rc = proto_register(&fanotify_proto, 0);
+
+ if (rc != 0)
+ goto out;
+
+ sock_register(&fanotify_family_ops);
+out:
+ return rc;
+}
+__initcall(fanotify_socket_register);
diff --git a/net/fanotify/af_fanotify.h b/net/fanotify/af_fanotify.h
new file mode 100644
index 0000000..a987d7b
--- /dev/null
+++ b/net/fanotify/af_fanotify.h
@@ -0,0 +1,20 @@
+#ifndef _LINUX_AF_FANOTIFY_H
+#define _LINUX_AF_FANOTIFY_H
+
+#ifdef __KERNEL__
+
+#include <linux/fanotify.h>
+#include <net/sock.h>
+
+struct fanotify_sock {
+ struct sock sock;
+ struct fanotify_group *group;
+};
+
+static inline struct fanotify_sock *fan_sk(struct sock *sock)
+{
+ return (struct fanotify_sock *)sock;
+}
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_AF_NET_H */

--
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/