PM / Wakeup: Show wakeup sources statistics in debugfs
[linux-2.6.git] / net / compat.c
index 9c97074..63d260e 100644 (file)
@@ -12,8 +12,8 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/gfp.h>
 #include <linux/fs.h>
-#include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/file.h>
 #include <linux/icmpv6.h>
 #include <linux/syscalls.h>
 #include <linux/filter.h>
 #include <linux/compat.h>
-#include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/security.h>
 
 #include <net/scm.h>
 #include <net/sock.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 #include <asm/uaccess.h>
 #include <net/compat.h>
 
@@ -35,11 +36,11 @@ static inline int iov_from_user_compat_to_kern(struct iovec *kiov,
 {
        int tot_len = 0;
 
-       while(niov > 0) {
+       while (niov > 0) {
                compat_uptr_t buf;
                compat_size_t len;
 
-               if(get_user(len, &uiov32->iov_len) ||
+               if (get_user(len, &uiov32->iov_len) ||
                   get_user(buf, &uiov32->iov_base)) {
                        tot_len = -EFAULT;
                        break;
@@ -75,16 +76,16 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
 
 /* I've named the args so it is easy to tell whose space the pointers are in. */
 int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
-                  char *kern_address, int mode)
+                  struct sockaddr *kern_address, int mode)
 {
        int tot_len;
 
-       if(kern_msg->msg_namelen) {
-               if(mode==VERIFY_READ) {
+       if (kern_msg->msg_namelen) {
+               if (mode == VERIFY_READ) {
                        int err = move_addr_to_kernel(kern_msg->msg_name,
                                                      kern_msg->msg_namelen,
                                                      kern_address);
-                       if(err < 0)
+                       if (err < 0)
                                return err;
                }
                kern_msg->msg_name = kern_address;
@@ -94,7 +95,7 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
        tot_len = iov_from_user_compat_to_kern(kern_iov,
                                          (struct compat_iovec __user *)kern_msg->msg_iov,
                                          kern_msg->msg_iovlen);
-       if(tot_len >= 0)
+       if (tot_len >= 0)
                kern_msg->msg_iov = kern_iov;
 
        return tot_len;
@@ -147,8 +148,8 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,
        kcmlen = 0;
        kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf;
        ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
-       while(ucmsg != NULL) {
-               if(get_user(ucmlen, &ucmsg->cmsg_len))
+       while (ucmsg != NULL) {
+               if (get_user(ucmlen, &ucmsg->cmsg_len))
                        return -EFAULT;
 
                /* Catch bogons. */
@@ -161,7 +162,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,
                kcmlen += tmp;
                ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen);
        }
-       if(kcmlen == 0)
+       if (kcmlen == 0)
                return -EINVAL;
 
        /* The kcmlen holds the 64-bit version of the control length.
@@ -177,7 +178,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,
        /* Now copy them over neatly. */
        memset(kcmsg, 0, kcmlen);
        ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg);
-       while(ucmsg != NULL) {
+       while (ucmsg != NULL) {
                if (__get_user(ucmlen, &ucmsg->cmsg_len))
                        goto Efault;
                if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg))
@@ -216,25 +217,38 @@ Efault:
 int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data)
 {
        struct compat_timeval ctv;
+       struct compat_timespec cts[3];
        struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control;
        struct compat_cmsghdr cmhdr;
        int cmlen;
 
-       if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
+       if (cm == NULL || kmsg->msg_controllen < sizeof(*cm)) {
                kmsg->msg_flags |= MSG_CTRUNC;
                return 0; /* XXX: return error? check spec. */
        }
 
-       if (level == SOL_SOCKET && type == SO_TIMESTAMP) {
+       if (level == SOL_SOCKET && type == SCM_TIMESTAMP) {
                struct timeval *tv = (struct timeval *)data;
                ctv.tv_sec = tv->tv_sec;
                ctv.tv_usec = tv->tv_usec;
                data = &ctv;
-               len = sizeof(struct compat_timeval);
+               len = sizeof(ctv);
+       }
+       if (level == SOL_SOCKET &&
+           (type == SCM_TIMESTAMPNS || type == SCM_TIMESTAMPING)) {
+               int count = type == SCM_TIMESTAMPNS ? 1 : 3;
+               int i;
+               struct timespec *ts = (struct timespec *)data;
+               for (i = 0; i < count; i++) {
+                       cts[i].tv_sec = ts[i].tv_sec;
+                       cts[i].tv_nsec = ts[i].tv_nsec;
+               }
+               data = &cts;
+               len = sizeof(cts[0]) * count;
        }
 
        cmlen = CMSG_COMPAT_LEN(len);
-       if(kmsg->msg_controllen < cmlen) {
+       if (kmsg->msg_controllen < cmlen) {
                kmsg->msg_flags |= MSG_CTRUNC;
                cmlen = kmsg->msg_controllen;
        }
@@ -242,11 +256,13 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat
        cmhdr.cmsg_type = type;
        cmhdr.cmsg_len = cmlen;
 
-       if(copy_to_user(cm, &cmhdr, sizeof cmhdr))
+       if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
                return -EFAULT;
-       if(copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr)))
+       if (copy_to_user(CMSG_COMPAT_DATA(cm), data, cmlen - sizeof(struct compat_cmsghdr)))
                return -EFAULT;
        cmlen = CMSG_COMPAT_SPACE(len);
+       if (kmsg->msg_controllen < cmlen)
+               cmlen = kmsg->msg_controllen;
        kmsg->msg_control += cmlen;
        kmsg->msg_controllen -= cmlen;
        return 0;
@@ -269,7 +285,8 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm)
                err = security_file_receive(fp[i]);
                if (err)
                        break;
-               err = get_unused_fd();
+               err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & kmsg->msg_flags
+                                         ? O_CLOEXEC : 0);
                if (err < 0)
                        break;
                new_fd = err;
@@ -307,107 +324,6 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm)
 }
 
 /*
- * For now, we assume that the compatibility and native version
- * of struct ipt_entry are the same - sfr.  FIXME
- */
-struct compat_ipt_replace {
-       char                    name[IPT_TABLE_MAXNAMELEN];
-       u32                     valid_hooks;
-       u32                     num_entries;
-       u32                     size;
-       u32                     hook_entry[NF_IP_NUMHOOKS];
-       u32                     underflow[NF_IP_NUMHOOKS];
-       u32                     num_counters;
-       compat_uptr_t           counters;       /* struct ipt_counters * */
-       struct ipt_entry        entries[0];
-};
-
-static int do_netfilter_replace(int fd, int level, int optname,
-                               char __user *optval, int optlen)
-{
-       struct compat_ipt_replace __user *urepl;
-       struct ipt_replace __user *repl_nat;
-       char name[IPT_TABLE_MAXNAMELEN];
-       u32 origsize, tmp32, num_counters;
-       unsigned int repl_nat_size;
-       int ret;
-       int i;
-       compat_uptr_t ucntrs;
-
-       urepl = (struct compat_ipt_replace __user *)optval;
-       if (get_user(origsize, &urepl->size))
-               return -EFAULT;
-
-       /* Hack: Causes ipchains to give correct error msg --RR */
-       if (optlen != sizeof(*urepl) + origsize)
-               return -ENOPROTOOPT;
-
-       /* XXX Assumes that size of ipt_entry is the same both in
-        *     native and compat environments.
-        */
-       repl_nat_size = sizeof(*repl_nat) + origsize;
-       repl_nat = compat_alloc_user_space(repl_nat_size);
-
-       ret = -EFAULT;
-       if (put_user(origsize, &repl_nat->size))
-               goto out;
-
-       if (!access_ok(VERIFY_READ, urepl, optlen) ||
-           !access_ok(VERIFY_WRITE, repl_nat, optlen))
-               goto out;
-
-       if (__copy_from_user(name, urepl->name, sizeof(urepl->name)) ||
-           __copy_to_user(repl_nat->name, name, sizeof(repl_nat->name)))
-               goto out;
-
-       if (__get_user(tmp32, &urepl->valid_hooks) ||
-           __put_user(tmp32, &repl_nat->valid_hooks))
-               goto out;
-
-       if (__get_user(tmp32, &urepl->num_entries) ||
-           __put_user(tmp32, &repl_nat->num_entries))
-               goto out;
-
-       if (__get_user(num_counters, &urepl->num_counters) ||
-           __put_user(num_counters, &repl_nat->num_counters))
-               goto out;
-
-       if (__get_user(ucntrs, &urepl->counters) ||
-           __put_user(compat_ptr(ucntrs), &repl_nat->counters))
-               goto out;
-
-       if (__copy_in_user(&repl_nat->entries[0],
-                          &urepl->entries[0],
-                          origsize))
-               goto out;
-
-       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
-               if (__get_user(tmp32, &urepl->hook_entry[i]) ||
-                   __put_user(tmp32, &repl_nat->hook_entry[i]) ||
-                   __get_user(tmp32, &urepl->underflow[i]) ||
-                   __put_user(tmp32, &repl_nat->underflow[i]))
-                       goto out;
-       }
-
-       /*
-        * Since struct ipt_counters just contains two u_int64_t members
-        * we can just do the access_ok check here and pass the (converted)
-        * pointer into the standard syscall.  We hope that the pointer is
-        * not misaligned ...
-        */
-       if (!access_ok(VERIFY_WRITE, compat_ptr(ucntrs),
-                      num_counters * sizeof(struct ipt_counters)))
-               goto out;
-
-
-       ret = sys_setsockopt(fd, level, optname,
-                            (char __user *)repl_nat, repl_nat_size);
-
-out:
-       return ret;
-}
-
-/*
  * A struct sock_filter is architecture independent.
  */
 struct compat_sock_fprog {
@@ -416,7 +332,7 @@ struct compat_sock_fprog {
 };
 
 static int do_set_attach_filter(struct socket *sock, int level, int optname,
-                               char __user *optval, int optlen)
+                               char __user *optval, unsigned int optlen)
 {
        struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval;
        struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog));
@@ -436,9 +352,9 @@ static int do_set_attach_filter(struct socket *sock, int level, int optname,
 }
 
 static int do_set_sock_timeout(struct socket *sock, int level,
-               int optname, char __user *optval, int optlen)
+               int optname, char __user *optval, unsigned int optlen)
 {
-       struct compat_timeval __user *up = (struct compat_timeval __user *) optval;
+       struct compat_timeval __user *up = (struct compat_timeval __user *)optval;
        struct timeval ktime;
        mm_segment_t old_fs;
        int err;
@@ -451,14 +367,14 @@ static int do_set_sock_timeout(struct socket *sock, int level,
                return -EFAULT;
        old_fs = get_fs();
        set_fs(KERNEL_DS);
-       err = sock_setsockopt(sock, level, optname, (char *) &ktime, sizeof(ktime));
+       err = sock_setsockopt(sock, level, optname, (char *)&ktime, sizeof(ktime));
        set_fs(old_fs);
 
        return err;
 }
 
 static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
-                               char __user *optval, int optlen)
+                               char __user *optval, unsigned int optlen)
 {
        if (optname == SO_ATTACH_FILTER)
                return do_set_attach_filter(sock, level, optname,
@@ -470,21 +386,13 @@ static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
 }
 
 asmlinkage long compat_sys_setsockopt(int fd, int level, int optname,
-                               char __user *optval, int optlen)
+                               char __user *optval, unsigned int optlen)
 {
        int err;
-       struct socket *sock;
+       struct socket *sock = sockfd_lookup(fd, &err);
 
-       if (level == SOL_IPV6 && optname == IPT_SO_SET_REPLACE)
-               return do_netfilter_replace(fd, level, optname,
-                                           optval, optlen);
-
-       if (optlen < 0)
-               return -EINVAL;
-
-       if ((sock = sockfd_lookup(fd, &err))!=NULL)
-       {
-               err = security_socket_setsockopt(sock,level,optname);
+       if (sock) {
+               err = security_socket_setsockopt(sock, level, optname);
                if (err) {
                        sockfd_put(sock);
                        return err;
@@ -544,32 +452,59 @@ static int compat_sock_getsockopt(struct socket *sock, int level, int optname,
 int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
 {
        struct compat_timeval __user *ctv =
-                       (struct compat_timeval __user*) userstamp;
+                       (struct compat_timeval __user *) userstamp;
        int err = -ENOENT;
+       struct timeval tv;
 
        if (!sock_flag(sk, SOCK_TIMESTAMP))
-               sock_enable_timestamp(sk);
-       if (sk->sk_stamp.tv_sec == -1)
+               sock_enable_timestamp(sk, SOCK_TIMESTAMP);
+       tv = ktime_to_timeval(sk->sk_stamp);
+       if (tv.tv_sec == -1)
                return err;
-       if (sk->sk_stamp.tv_sec == 0)
-               do_gettimeofday(&sk->sk_stamp);
-       if (put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec) ||
-                       put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec))
+       if (tv.tv_sec == 0) {
+               sk->sk_stamp = ktime_get_real();
+               tv = ktime_to_timeval(sk->sk_stamp);
+       }
+       err = 0;
+       if (put_user(tv.tv_sec, &ctv->tv_sec) ||
+                       put_user(tv.tv_usec, &ctv->tv_usec))
                err = -EFAULT;
        return err;
 }
 EXPORT_SYMBOL(compat_sock_get_timestamp);
 
+int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
+{
+       struct compat_timespec __user *ctv =
+                       (struct compat_timespec __user *) userstamp;
+       int err = -ENOENT;
+       struct timespec ts;
+
+       if (!sock_flag(sk, SOCK_TIMESTAMP))
+               sock_enable_timestamp(sk, SOCK_TIMESTAMP);
+       ts = ktime_to_timespec(sk->sk_stamp);
+       if (ts.tv_sec == -1)
+               return err;
+       if (ts.tv_sec == 0) {
+               sk->sk_stamp = ktime_get_real();
+               ts = ktime_to_timespec(sk->sk_stamp);
+       }
+       err = 0;
+       if (put_user(ts.tv_sec, &ctv->tv_sec) ||
+                       put_user(ts.tv_nsec, &ctv->tv_nsec))
+               err = -EFAULT;
+       return err;
+}
+EXPORT_SYMBOL(compat_sock_get_timestampns);
+
 asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
                                char __user *optval, int __user *optlen)
 {
        int err;
-       struct socket *sock;
+       struct socket *sock = sockfd_lookup(fd, &err);
 
-       if ((sock = sockfd_lookup(fd, &err))!=NULL)
-       {
-               err = security_socket_getsockopt(sock, level,
-                                                          optname);
+       if (sock) {
+               err = security_socket_getsockopt(sock, level, optname);
                if (err) {
                        sockfd_put(sock);
                        return err;
@@ -588,11 +523,209 @@ asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
        }
        return err;
 }
+
+struct compat_group_req {
+       __u32                            gr_interface;
+       struct __kernel_sockaddr_storage gr_group
+               __attribute__ ((aligned(4)));
+} __packed;
+
+struct compat_group_source_req {
+       __u32                            gsr_interface;
+       struct __kernel_sockaddr_storage gsr_group
+               __attribute__ ((aligned(4)));
+       struct __kernel_sockaddr_storage gsr_source
+               __attribute__ ((aligned(4)));
+} __packed;
+
+struct compat_group_filter {
+       __u32                            gf_interface;
+       struct __kernel_sockaddr_storage gf_group
+               __attribute__ ((aligned(4)));
+       __u32                            gf_fmode;
+       __u32                            gf_numsrc;
+       struct __kernel_sockaddr_storage gf_slist[1]
+               __attribute__ ((aligned(4)));
+} __packed;
+
+#define __COMPAT_GF0_SIZE (sizeof(struct compat_group_filter) - \
+                       sizeof(struct __kernel_sockaddr_storage))
+
+
+int compat_mc_setsockopt(struct sock *sock, int level, int optname,
+       char __user *optval, unsigned int optlen,
+       int (*setsockopt)(struct sock *, int, int, char __user *, unsigned int))
+{
+       char __user     *koptval = optval;
+       int             koptlen = optlen;
+
+       switch (optname) {
+       case MCAST_JOIN_GROUP:
+       case MCAST_LEAVE_GROUP:
+       {
+               struct compat_group_req __user *gr32 = (void *)optval;
+               struct group_req __user *kgr =
+                       compat_alloc_user_space(sizeof(struct group_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gr32, sizeof(*gr32)) ||
+                   !access_ok(VERIFY_WRITE, kgr, sizeof(struct group_req)) ||
+                   __get_user(interface, &gr32->gr_interface) ||
+                   __put_user(interface, &kgr->gr_interface) ||
+                   copy_in_user(&kgr->gr_group, &gr32->gr_group,
+                               sizeof(kgr->gr_group)))
+                       return -EFAULT;
+               koptval = (char __user *)kgr;
+               koptlen = sizeof(struct group_req);
+               break;
+       }
+       case MCAST_JOIN_SOURCE_GROUP:
+       case MCAST_LEAVE_SOURCE_GROUP:
+       case MCAST_BLOCK_SOURCE:
+       case MCAST_UNBLOCK_SOURCE:
+       {
+               struct compat_group_source_req __user *gsr32 = (void *)optval;
+               struct group_source_req __user *kgsr = compat_alloc_user_space(
+                       sizeof(struct group_source_req));
+               u32 interface;
+
+               if (!access_ok(VERIFY_READ, gsr32, sizeof(*gsr32)) ||
+                   !access_ok(VERIFY_WRITE, kgsr,
+                       sizeof(struct group_source_req)) ||
+                   __get_user(interface, &gsr32->gsr_interface) ||
+                   __put_user(interface, &kgsr->gsr_interface) ||
+                   copy_in_user(&kgsr->gsr_group, &gsr32->gsr_group,
+                               sizeof(kgsr->gsr_group)) ||
+                   copy_in_user(&kgsr->gsr_source, &gsr32->gsr_source,
+                               sizeof(kgsr->gsr_source)))
+                       return -EFAULT;
+               koptval = (char __user *)kgsr;
+               koptlen = sizeof(struct group_source_req);
+               break;
+       }
+       case MCAST_MSFILTER:
+       {
+               struct compat_group_filter __user *gf32 = (void *)optval;
+               struct group_filter __user *kgf;
+               u32 interface, fmode, numsrc;
+
+               if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) ||
+                   __get_user(interface, &gf32->gf_interface) ||
+                   __get_user(fmode, &gf32->gf_fmode) ||
+                   __get_user(numsrc, &gf32->gf_numsrc))
+                       return -EFAULT;
+               koptlen = optlen + sizeof(struct group_filter) -
+                               sizeof(struct compat_group_filter);
+               if (koptlen < GROUP_FILTER_SIZE(numsrc))
+                       return -EINVAL;
+               kgf = compat_alloc_user_space(koptlen);
+               if (!access_ok(VERIFY_WRITE, kgf, koptlen) ||
+                   __put_user(interface, &kgf->gf_interface) ||
+                   __put_user(fmode, &kgf->gf_fmode) ||
+                   __put_user(numsrc, &kgf->gf_numsrc) ||
+                   copy_in_user(&kgf->gf_group, &gf32->gf_group,
+                               sizeof(kgf->gf_group)) ||
+                   (numsrc && copy_in_user(kgf->gf_slist, gf32->gf_slist,
+                               numsrc * sizeof(kgf->gf_slist[0]))))
+                       return -EFAULT;
+               koptval = (char __user *)kgf;
+               break;
+       }
+
+       default:
+               break;
+       }
+       return setsockopt(sock, level, optname, koptval, koptlen);
+}
+EXPORT_SYMBOL(compat_mc_setsockopt);
+
+int compat_mc_getsockopt(struct sock *sock, int level, int optname,
+       char __user *optval, int __user *optlen,
+       int (*getsockopt)(struct sock *, int, int, char __user *, int __user *))
+{
+       struct compat_group_filter __user *gf32 = (void *)optval;
+       struct group_filter __user *kgf;
+       int __user      *koptlen;
+       u32 interface, fmode, numsrc;
+       int klen, ulen, err;
+
+       if (optname != MCAST_MSFILTER)
+               return getsockopt(sock, level, optname, optval, optlen);
+
+       koptlen = compat_alloc_user_space(sizeof(*koptlen));
+       if (!access_ok(VERIFY_READ, optlen, sizeof(*optlen)) ||
+           __get_user(ulen, optlen))
+               return -EFAULT;
+
+       /* adjust len for pad */
+       klen = ulen + sizeof(*kgf) - sizeof(*gf32);
+
+       if (klen < GROUP_FILTER_SIZE(0))
+               return -EINVAL;
+
+       if (!access_ok(VERIFY_WRITE, koptlen, sizeof(*koptlen)) ||
+           __put_user(klen, koptlen))
+               return -EFAULT;
+
+       /* have to allow space for previous compat_alloc_user_space, too */
+       kgf = compat_alloc_user_space(klen+sizeof(*optlen));
+
+       if (!access_ok(VERIFY_READ, gf32, __COMPAT_GF0_SIZE) ||
+           __get_user(interface, &gf32->gf_interface) ||
+           __get_user(fmode, &gf32->gf_fmode) ||
+           __get_user(numsrc, &gf32->gf_numsrc) ||
+           __put_user(interface, &kgf->gf_interface) ||
+           __put_user(fmode, &kgf->gf_fmode) ||
+           __put_user(numsrc, &kgf->gf_numsrc) ||
+           copy_in_user(&kgf->gf_group, &gf32->gf_group, sizeof(kgf->gf_group)))
+               return -EFAULT;
+
+       err = getsockopt(sock, level, optname, (char __user *)kgf, koptlen);
+       if (err)
+               return err;
+
+       if (!access_ok(VERIFY_READ, koptlen, sizeof(*koptlen)) ||
+           __get_user(klen, koptlen))
+               return -EFAULT;
+
+       ulen = klen - (sizeof(*kgf)-sizeof(*gf32));
+
+       if (!access_ok(VERIFY_WRITE, optlen, sizeof(*optlen)) ||
+           __put_user(ulen, optlen))
+               return -EFAULT;
+
+       if (!access_ok(VERIFY_READ, kgf, klen) ||
+           !access_ok(VERIFY_WRITE, gf32, ulen) ||
+           __get_user(interface, &kgf->gf_interface) ||
+           __get_user(fmode, &kgf->gf_fmode) ||
+           __get_user(numsrc, &kgf->gf_numsrc) ||
+           __put_user(interface, &gf32->gf_interface) ||
+           __put_user(fmode, &gf32->gf_fmode) ||
+           __put_user(numsrc, &gf32->gf_numsrc))
+               return -EFAULT;
+       if (numsrc) {
+               int copylen;
+
+               klen -= GROUP_FILTER_SIZE(0);
+               copylen = numsrc * sizeof(gf32->gf_slist[0]);
+               if (copylen > klen)
+                       copylen = klen;
+               if (copy_in_user(gf32->gf_slist, kgf->gf_slist, copylen))
+                       return -EFAULT;
+       }
+       return err;
+}
+EXPORT_SYMBOL(compat_mc_getsockopt);
+
+
 /* Argument list sizes for compat_sys_socketcall */
 #define AL(x) ((x) * sizeof(u32))
-static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
-                               AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
-                               AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
+static unsigned char nas[20] = {
+       AL(0), AL(3), AL(3), AL(3), AL(2), AL(3),
+       AL(3), AL(3), AL(4), AL(4), AL(4), AL(6),
+       AL(6), AL(2), AL(5), AL(5), AL(3), AL(3),
+       AL(4), AL(5)
+};
 #undef AL
 
 asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags)
@@ -605,20 +738,54 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns
        return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
 }
 
+asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags)
+{
+       return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT);
+}
+
+asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len,
+                                   unsigned flags, struct sockaddr __user *addr,
+                                   int __user *addrlen)
+{
+       return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen);
+}
+
+asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg,
+                                   unsigned vlen, unsigned int flags,
+                                   struct compat_timespec __user *timeout)
+{
+       int datagrams;
+       struct timespec ktspec;
+
+       if (timeout == NULL)
+               return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
+                                     flags | MSG_CMSG_COMPAT, NULL);
+
+       if (get_compat_timespec(&ktspec, timeout))
+               return -EFAULT;
+
+       datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
+                                  flags | MSG_CMSG_COMPAT, &ktspec);
+       if (datagrams > 0 && put_compat_timespec(&ktspec, timeout))
+               datagrams = -EFAULT;
+
+       return datagrams;
+}
+
 asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
 {
        int ret;
        u32 a[6];
        u32 a0, a1;
 
-       if (call < SYS_SOCKET || call > SYS_RECVMSG)
+       if (call < SYS_SOCKET || call > SYS_RECVMMSG)
                return -EINVAL;
        if (copy_from_user(a, args, nas[call]))
                return -EFAULT;
        a0 = a[0];
        a1 = a[1];
 
-       switch(call) {
+       switch (call) {
        case SYS_SOCKET:
                ret = sys_socket(a0, a1, a[2]);
                break;
@@ -632,7 +799,7 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
                ret = sys_listen(a0, a1);
                break;
        case SYS_ACCEPT:
-               ret = sys_accept(a0, compat_ptr(a1), compat_ptr(a[2]));
+               ret = sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), 0);
                break;
        case SYS_GETSOCKNAME:
                ret = sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2]));
@@ -650,13 +817,14 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
                ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]);
                break;
        case SYS_RECV:
-               ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]);
+               ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]);
                break;
        case SYS_RECVFROM:
-               ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5]));
+               ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
+                                         compat_ptr(a[4]), compat_ptr(a[5]));
                break;
        case SYS_SHUTDOWN:
-               ret = sys_shutdown(a0,a1);
+               ret = sys_shutdown(a0, a1);
                break;
        case SYS_SETSOCKOPT:
                ret = compat_sys_setsockopt(a0, a1, a[2],
@@ -672,6 +840,13 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
        case SYS_RECVMSG:
                ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]);
                break;
+       case SYS_RECVMMSG:
+               ret = compat_sys_recvmmsg(a0, compat_ptr(a1), a[2], a[3],
+                                         compat_ptr(a[4]));
+               break;
+       case SYS_ACCEPT4:
+               ret = sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]);
+               break;
        default:
                ret = -EINVAL;
                break;