From 3fdadf7d27e3fbcf72930941884387d1f4936f04 Mon Sep 17 00:00:00 2001 From: Dmitry Mishin Date: Mon, 20 Mar 2006 22:45:21 -0800 Subject: [NET]: {get|set}sockopt compatibility layer This patch extends {get|set}sockopt compatibility layer in order to move protocol specific parts to their place and avoid huge universal net/compat.c file in the future. Signed-off-by: Dmitry Mishin Signed-off-by: David S. Miller --- net/compat.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 18 deletions(-) (limited to 'net/compat.c') diff --git a/net/compat.c b/net/compat.c index e593dace2fdb..13177a1a4b39 100644 --- a/net/compat.c +++ b/net/compat.c @@ -416,7 +416,7 @@ struct compat_sock_fprog { compat_uptr_t filter; /* struct sock_filter * */ }; -static int do_set_attach_filter(int fd, int level, int optname, +static int do_set_attach_filter(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; @@ -432,11 +432,12 @@ static int do_set_attach_filter(int fd, int level, int optname, __put_user(compat_ptr(ptr), &kfprog->filter)) return -EFAULT; - return sys_setsockopt(fd, level, optname, (char __user *)kfprog, + return sock_setsockopt(sock, level, optname, (char __user *)kfprog, sizeof(struct sock_fprog)); } -static int do_set_sock_timeout(int fd, int level, int optname, char __user *optval, int optlen) +static int do_set_sock_timeout(struct socket *sock, int level, + int optname, char __user *optval, int optlen) { struct compat_timeval __user *up = (struct compat_timeval __user *) optval; struct timeval ktime; @@ -451,30 +452,61 @@ static int do_set_sock_timeout(int fd, int level, int optname, char __user *optv return -EFAULT; old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_setsockopt(fd, 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) +{ + if (optname == SO_ATTACH_FILTER) + return do_set_attach_filter(sock, level, optname, + optval, optlen); + if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) + return do_set_sock_timeout(sock, level, optname, optval, optlen); + + return sock_setsockopt(sock, level, optname, optval, optlen); +} + asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen) { + int err; + struct socket *sock; + /* SO_SET_REPLACE seems to be the same in all levels */ if (optname == IPT_SO_SET_REPLACE) return do_netfilter_replace(fd, level, optname, optval, optlen); - if (level == SOL_SOCKET && optname == SO_ATTACH_FILTER) - return do_set_attach_filter(fd, level, optname, - optval, optlen); - if (level == SOL_SOCKET && - (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) - return do_set_sock_timeout(fd, level, optname, optval, optlen); - return sys_setsockopt(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 (err) { + sockfd_put(sock); + return err; + } + + if (level == SOL_SOCKET) + err = compat_sock_setsockopt(sock, level, + optname, optval, optlen); + else if (sock->ops->compat_setsockopt) + err = sock->ops->compat_setsockopt(sock, level, + optname, optval, optlen); + else + err = sock->ops->setsockopt(sock, level, + optname, optval, optlen); + sockfd_put(sock); + } + return err; } -static int do_get_sock_timeout(int fd, int level, int optname, +static int do_get_sock_timeout(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct compat_timeval __user *up; @@ -490,7 +522,7 @@ static int do_get_sock_timeout(int fd, int level, int optname, len = sizeof(ktime); old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_getsockopt(fd, level, optname, (char *) &ktime, &len); + err = sock_getsockopt(sock, level, optname, (char *) &ktime, &len); set_fs(old_fs); if (!err) { @@ -503,15 +535,42 @@ static int do_get_sock_timeout(int fd, int level, int optname, return err; } -asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, +static int compat_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { - if (level == SOL_SOCKET && - (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) - return do_get_sock_timeout(fd, level, optname, optval, optlen); - return sys_getsockopt(fd, level, optname, optval, optlen); + if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) + return do_get_sock_timeout(sock, level, optname, optval, optlen); + return sock_getsockopt(sock, level, optname, optval, optlen); } +asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, + char __user *optval, int __user *optlen) +{ + int err; + struct socket *sock; + + if ((sock = sockfd_lookup(fd, &err))!=NULL) + { + err = security_socket_getsockopt(sock, level, + optname); + if (err) { + sockfd_put(sock); + return err; + } + + if (level == SOL_SOCKET) + err = compat_sock_getsockopt(sock, level, + optname, optval, optlen); + else if (sock->ops->compat_getsockopt) + err = sock->ops->compat_getsockopt(sock, level, + optname, optval, optlen); + else + err = sock->ops->getsockopt(sock, level, + optname, optval, optlen); + sockfd_put(sock); + } + return err; +} /* 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), -- cgit v1.2.2