lkml.org 
[lkml]   [2008]   [Nov]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH =-v3 06/21] fanotify: add a userspace interface for fanotify notifications
    Date
    notifications from fanotify need a userspace interface.  We are going to
    try to do it with sockets. Scary....

    Signed-off-by: Eric Paris <eparis@redhat.com>
    ---

    include/linux/fanotify.h | 29 ++++-
    include/linux/socket.h | 5 +
    net/Makefile | 1
    net/core/sock.c | 6 +
    net/fanotify/Makefile | 5 +
    net/fanotify/af_fanotify.c | 240 ++++++++++++++++++++++++++++++++++++++++++++
    net/fanotify/af_fanotify.h | 20 ++++
    7 files changed, 294 insertions(+), 12 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..76d88fd 100644
    --- a/include/linux/fanotify.h
    +++ b/include/linux/fanotify.h
    @@ -34,17 +34,23 @@
    FAN_CLOSE |\
    FAN_OPEN)

    -#ifdef __KERNEL__
    #include <linux/types.h>
    -#else
    -#include <stdint.h>
    -#include <sys/types.h>
    -#endif

    +struct fanotify_addr {
    + __u32 group_num;
    + __u32 mask;
    + __u32 timeout;
    + __u32 unused[9];
    +} __attribute__((packed));
    +
    +/* struct used for FANOTIFY_GET_EVENT */
    struct fanotify_event_metadata {
    - int32_t fd;
    - uint32_t mask;
    -};
    + __s32 fd;
    + __u32 mask;
    +} __attribute__((packed));
    +
    +/* fanotify getsockopt optvals */
    +#define FANOTIFY_GET_EVENT 1

    #ifdef __KERNEL__

    @@ -74,6 +80,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 +98,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..7474cf1
    --- /dev/null
    +++ b/net/fanotify/af_fanotify.c
    @@ -0,0 +1,240 @@
    +#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/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;
    +
    + if (sock->state != SS_UNCONNECTED)
    + 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 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;
    + union {
    + struct fanotify_event_metadata event_metadata;
    + } data;
    + int len, ret = 0;
    +
    + if (get_user(len, optlen))
    + return -EFAULT;
    +
    + if (sock->state != SS_CONNECTED)
    + return -EBADF;
    +
    + if (level != SOL_FANOTIFY)
    + return -ENOPROTOOPT;
    +
    + fan_sock = fan_sk(sock->sk);
    + group = fan_sock->group;
    +
    + switch (optname) {
    + case FANOTIFY_GET_EVENT:
    + if (len < sizeof(struct fanotify_event_metadata))
    + return -ENOMEM;
    + else
    + len = sizeof(struct fanotify_event_metadata);
    + ret = fanotify_create_event_fd(group, &data.event_metadata, !!(sock->file->f_flags & O_NONBLOCK));
    + if (ret)
    + break;
    +
    + ret = copy_to_user(optval, &data.event_metadata, sizeof(struct fanotify_event_metadata));
    + break;
    + default:
    + return -ENOPROTOOPT;
    + };
    +
    + if (!ret)
    + ret = put_user(len, optlen);
    +
    + 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 = sock_no_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 */


    \
     
     \ /
      Last update: 2008-11-12 17:15    [W:0.042 / U:58.836 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site