2007-02-09 17:25:31 +03:00
/*
2005-04-17 02:20:36 +04:00
* 32 bit Socket syscall emulation . Based on arch / sparc64 / kernel / sys_sparc32 . c .
*
* Copyright ( C ) 2000 VA Linux Co
* Copyright ( C ) 2000 Don Dugger < n0ano @ valinux . com >
* Copyright ( C ) 1999 Arun Sharma < arun . sharma @ intel . com >
* Copyright ( C ) 1997 , 1998 Jakub Jelinek ( jj @ sunsite . mff . cuni . cz )
* Copyright ( C ) 1997 David S . Miller ( davem @ caip . rutgers . edu )
* Copyright ( C ) 2000 Hewlett - Packard Co .
* Copyright ( C ) 2000 David Mosberger - Tang < davidm @ hpl . hp . com >
2007-02-09 17:25:31 +03:00
* Copyright ( C ) 2000 , 2001 Andi Kleen , SuSE Labs
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/types.h>
# include <linux/file.h>
# include <linux/icmpv6.h>
# include <linux/socket.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 <asm/uaccess.h>
# include <net/compat.h>
static inline int iov_from_user_compat_to_kern ( struct iovec * kiov ,
struct compat_iovec __user * uiov32 ,
int niov )
{
int tot_len = 0 ;
while ( niov > 0 ) {
compat_uptr_t buf ;
compat_size_t len ;
if ( get_user ( len , & uiov32 - > iov_len ) | |
get_user ( buf , & uiov32 - > iov_base ) ) {
tot_len = - EFAULT ;
break ;
}
tot_len + = len ;
kiov - > iov_base = compat_ptr ( buf ) ;
kiov - > iov_len = ( __kernel_size_t ) len ;
uiov32 + + ;
kiov + + ;
niov - - ;
}
return tot_len ;
}
int get_compat_msghdr ( struct msghdr * kmsg , struct compat_msghdr __user * umsg )
{
compat_uptr_t tmp1 , tmp2 , tmp3 ;
if ( ! access_ok ( VERIFY_READ , umsg , sizeof ( * umsg ) ) | |
__get_user ( tmp1 , & umsg - > msg_name ) | |
__get_user ( kmsg - > msg_namelen , & umsg - > msg_namelen ) | |
__get_user ( tmp2 , & umsg - > msg_iov ) | |
__get_user ( kmsg - > msg_iovlen , & umsg - > msg_iovlen ) | |
__get_user ( tmp3 , & umsg - > msg_control ) | |
__get_user ( kmsg - > msg_controllen , & umsg - > msg_controllen ) | |
__get_user ( kmsg - > msg_flags , & umsg - > msg_flags ) )
return - EFAULT ;
kmsg - > msg_name = compat_ptr ( tmp1 ) ;
kmsg - > msg_iov = compat_ptr ( tmp2 ) ;
kmsg - > msg_control = compat_ptr ( tmp3 ) ;
return 0 ;
}
/* 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 )
{
int tot_len ;
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 )
return err ;
}
kern_msg - > msg_name = kern_address ;
} else
kern_msg - > msg_name = NULL ;
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 )
kern_msg - > msg_iov = kern_iov ;
return tot_len ;
}
/* Bleech... */
# define CMSG_COMPAT_ALIGN(len) ALIGN((len), sizeof(s32))
# define CMSG_COMPAT_DATA(cmsg) \
( ( void __user * ) ( ( char __user * ) ( cmsg ) + CMSG_COMPAT_ALIGN ( sizeof ( struct compat_cmsghdr ) ) ) )
# define CMSG_COMPAT_SPACE(len) \
( CMSG_COMPAT_ALIGN ( sizeof ( struct compat_cmsghdr ) ) + CMSG_COMPAT_ALIGN ( len ) )
# define CMSG_COMPAT_LEN(len) \
( CMSG_COMPAT_ALIGN ( sizeof ( struct compat_cmsghdr ) ) + ( len ) )
# define CMSG_COMPAT_FIRSTHDR(msg) \
( ( ( msg ) - > msg_controllen ) > = sizeof ( struct compat_cmsghdr ) ? \
( struct compat_cmsghdr __user * ) ( ( msg ) - > msg_control ) : \
( struct compat_cmsghdr __user * ) NULL )
# define CMSG_COMPAT_OK(ucmlen, ucmsg, mhdr) \
( ( ucmlen ) > = sizeof ( struct compat_cmsghdr ) & & \
( ucmlen ) < = ( unsigned long ) \
( ( mhdr ) - > msg_controllen - \
( ( char * ) ( ucmsg ) - ( char * ) ( mhdr ) - > msg_control ) ) )
static inline struct compat_cmsghdr __user * cmsg_compat_nxthdr ( struct msghdr * msg ,
struct compat_cmsghdr __user * cmsg , int cmsg_len )
{
char __user * ptr = ( char __user * ) cmsg + CMSG_COMPAT_ALIGN ( cmsg_len ) ;
if ( ( unsigned long ) ( ptr + 1 - ( char __user * ) msg - > msg_control ) >
msg - > msg_controllen )
return NULL ;
return ( struct compat_cmsghdr __user * ) ptr ;
}
/* There is a lot of hair here because the alignment rules (and
* thus placement ) of cmsg headers and length are different for
* 32 - bit apps . - DaveM
*/
2005-09-08 05:28:51 +04:00
int cmsghdr_from_user_compat_to_kern ( struct msghdr * kmsg , struct sock * sk ,
2005-04-17 02:20:36 +04:00
unsigned char * stackbuf , int stackbuf_size )
{
struct compat_cmsghdr __user * ucmsg ;
struct cmsghdr * kcmsg , * kcmsg_base ;
compat_size_t ucmlen ;
__kernel_size_t kcmlen , tmp ;
2005-09-08 05:28:51 +04:00
int err = - EFAULT ;
2005-04-17 02:20:36 +04:00
kcmlen = 0 ;
kcmsg_base = kcmsg = ( struct cmsghdr * ) stackbuf ;
ucmsg = CMSG_COMPAT_FIRSTHDR ( kmsg ) ;
while ( ucmsg ! = NULL ) {
if ( get_user ( ucmlen , & ucmsg - > cmsg_len ) )
return - EFAULT ;
/* Catch bogons. */
if ( ! CMSG_COMPAT_OK ( ucmlen , ucmsg , kmsg ) )
return - EINVAL ;
tmp = ( ( ucmlen - CMSG_COMPAT_ALIGN ( sizeof ( * ucmsg ) ) ) +
CMSG_ALIGN ( sizeof ( struct cmsghdr ) ) ) ;
2005-09-08 05:28:51 +04:00
tmp = CMSG_ALIGN ( tmp ) ;
2005-04-17 02:20:36 +04:00
kcmlen + = tmp ;
ucmsg = cmsg_compat_nxthdr ( kmsg , ucmsg , ucmlen ) ;
}
if ( kcmlen = = 0 )
return - EINVAL ;
/* The kcmlen holds the 64-bit version of the control length.
* It may not be modified as we do not stick it into the kmsg
* until we have successfully copied over all of the data
* from the user .
*/
2005-09-08 05:28:51 +04:00
if ( kcmlen > stackbuf_size )
kcmsg_base = kcmsg = sock_kmalloc ( sk , kcmlen , GFP_KERNEL ) ;
if ( kcmsg = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOBUFS ;
/* Now copy them over neatly. */
memset ( kcmsg , 0 , kcmlen ) ;
ucmsg = CMSG_COMPAT_FIRSTHDR ( kmsg ) ;
while ( ucmsg ! = NULL ) {
2005-09-08 05:28:51 +04:00
if ( __get_user ( ucmlen , & ucmsg - > cmsg_len ) )
goto Efault ;
if ( ! CMSG_COMPAT_OK ( ucmlen , ucmsg , kmsg ) )
goto Einval ;
2005-04-17 02:20:36 +04:00
tmp = ( ( ucmlen - CMSG_COMPAT_ALIGN ( sizeof ( * ucmsg ) ) ) +
CMSG_ALIGN ( sizeof ( struct cmsghdr ) ) ) ;
2005-09-08 05:28:51 +04:00
if ( ( char * ) kcmsg_base + kcmlen - ( char * ) kcmsg < CMSG_ALIGN ( tmp ) )
goto Einval ;
2005-04-17 02:20:36 +04:00
kcmsg - > cmsg_len = tmp ;
2005-09-08 05:28:51 +04:00
tmp = CMSG_ALIGN ( tmp ) ;
if ( __get_user ( kcmsg - > cmsg_level , & ucmsg - > cmsg_level ) | |
__get_user ( kcmsg - > cmsg_type , & ucmsg - > cmsg_type ) | |
copy_from_user ( CMSG_DATA ( kcmsg ) ,
CMSG_COMPAT_DATA ( ucmsg ) ,
( ucmlen - CMSG_COMPAT_ALIGN ( sizeof ( * ucmsg ) ) ) ) )
goto Efault ;
2005-04-17 02:20:36 +04:00
/* Advance. */
2005-09-08 05:28:51 +04:00
kcmsg = ( struct cmsghdr * ) ( ( char * ) kcmsg + tmp ) ;
2005-04-17 02:20:36 +04:00
ucmsg = cmsg_compat_nxthdr ( kmsg , ucmsg , ucmlen ) ;
}
/* Ok, looks like we made it. Hook it up and return success. */
kmsg - > msg_control = kcmsg_base ;
kmsg - > msg_controllen = kcmlen ;
return 0 ;
2005-09-08 05:28:51 +04:00
Einval :
err = - EINVAL ;
Efault :
if ( kcmsg_base ! = ( struct cmsghdr * ) stackbuf )
sock_kfree_s ( sk , kcmsg_base , kcmlen ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
int put_cmsg_compat ( struct msghdr * kmsg , int level , int type , int len , void * data )
{
struct compat_timeval ctv ;
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 ) ) {
kmsg - > msg_flags | = MSG_CTRUNC ;
return 0 ; /* XXX: return error? check spec. */
}
2007-02-09 17:25:31 +03:00
if ( level = = SOL_SOCKET & & type = = SO_TIMESTAMP ) {
2005-04-17 02:20:36 +04:00
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 ) ;
2007-02-09 17:25:31 +03:00
}
2005-04-17 02:20:36 +04:00
cmlen = CMSG_COMPAT_LEN ( len ) ;
if ( kmsg - > msg_controllen < cmlen ) {
kmsg - > msg_flags | = MSG_CTRUNC ;
cmlen = kmsg - > msg_controllen ;
}
cmhdr . cmsg_level = level ;
cmhdr . cmsg_type = type ;
cmhdr . cmsg_len = cmlen ;
if ( copy_to_user ( cm , & cmhdr , sizeof cmhdr ) )
return - EFAULT ;
if ( copy_to_user ( CMSG_COMPAT_DATA ( cm ) , data , cmlen - sizeof ( struct compat_cmsghdr ) ) )
return - EFAULT ;
cmlen = CMSG_COMPAT_SPACE ( len ) ;
kmsg - > msg_control + = cmlen ;
kmsg - > msg_controllen - = cmlen ;
return 0 ;
}
void scm_detach_fds_compat ( struct msghdr * kmsg , struct scm_cookie * scm )
{
struct compat_cmsghdr __user * cm = ( struct compat_cmsghdr __user * ) kmsg - > msg_control ;
int fdmax = ( kmsg - > msg_controllen - sizeof ( struct compat_cmsghdr ) ) / sizeof ( int ) ;
int fdnum = scm - > fp - > count ;
struct file * * fp = scm - > fp - > fp ;
int __user * cmfptr ;
int err = 0 , i ;
if ( fdnum < fdmax )
fdmax = fdnum ;
for ( i = 0 , cmfptr = ( int __user * ) CMSG_COMPAT_DATA ( cm ) ; i < fdmax ; i + + , cmfptr + + ) {
int new_fd ;
err = security_file_receive ( fp [ i ] ) ;
if ( err )
break ;
err = get_unused_fd ( ) ;
if ( err < 0 )
break ;
new_fd = err ;
err = put_user ( new_fd , cmfptr ) ;
if ( err ) {
put_unused_fd ( new_fd ) ;
break ;
}
/* Bump the usage count and install the file. */
get_file ( fp [ i ] ) ;
fd_install ( new_fd , fp [ i ] ) ;
}
if ( i > 0 ) {
int cmlen = CMSG_COMPAT_LEN ( i * sizeof ( int ) ) ;
2006-10-10 08:42:14 +04:00
err = put_user ( SOL_SOCKET , & cm - > cmsg_level ) ;
2005-04-17 02:20:36 +04:00
if ( ! err )
err = put_user ( SCM_RIGHTS , & cm - > cmsg_type ) ;
if ( ! err )
err = put_user ( cmlen , & cm - > cmsg_len ) ;
if ( ! err ) {
cmlen = CMSG_COMPAT_SPACE ( i * sizeof ( int ) ) ;
kmsg - > msg_control + = cmlen ;
kmsg - > msg_controllen - = cmlen ;
}
}
if ( i < fdnum )
kmsg - > msg_flags | = MSG_CTRUNC ;
/*
* All of the files that fit in the message have had their
* usage counts incremented , so we just free the list .
*/
__scm_destroy ( 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 {
u16 len ;
compat_uptr_t filter ; /* struct sock_filter * */
} ;
2006-03-21 09:45:21 +03:00
static int do_set_attach_filter ( struct socket * sock , int level , int optname ,
2005-04-17 02:20:36 +04:00
char __user * optval , int optlen )
{
struct compat_sock_fprog __user * fprog32 = ( struct compat_sock_fprog __user * ) optval ;
2007-02-09 17:25:31 +03:00
struct sock_fprog __user * kfprog = compat_alloc_user_space ( sizeof ( struct sock_fprog ) ) ;
2005-04-17 02:20:36 +04:00
compat_uptr_t ptr ;
u16 len ;
if ( ! access_ok ( VERIFY_READ , fprog32 , sizeof ( * fprog32 ) ) | |
! access_ok ( VERIFY_WRITE , kfprog , sizeof ( struct sock_fprog ) ) | |
__get_user ( len , & fprog32 - > len ) | |
__get_user ( ptr , & fprog32 - > filter ) | |
__put_user ( len , & kfprog - > len ) | |
__put_user ( compat_ptr ( ptr ) , & kfprog - > filter ) )
return - EFAULT ;
2006-03-21 09:45:21 +03:00
return sock_setsockopt ( sock , level , optname , ( char __user * ) kfprog ,
2005-04-17 02:20:36 +04:00
sizeof ( struct sock_fprog ) ) ;
}
2006-03-21 09:45:21 +03:00
static int do_set_sock_timeout ( struct socket * sock , int level ,
int optname , char __user * optval , int optlen )
2005-04-17 02:20:36 +04:00
{
struct compat_timeval __user * up = ( struct compat_timeval __user * ) optval ;
struct timeval ktime ;
mm_segment_t old_fs ;
int err ;
if ( optlen < sizeof ( * up ) )
return - EINVAL ;
if ( ! access_ok ( VERIFY_READ , up , sizeof ( * up ) ) | |
__get_user ( ktime . tv_sec , & up - > tv_sec ) | |
__get_user ( ktime . tv_usec , & up - > tv_usec ) )
return - EFAULT ;
old_fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
2006-03-21 09:45:21 +03:00
err = sock_setsockopt ( sock , level , optname , ( char * ) & ktime , sizeof ( ktime ) ) ;
2005-04-17 02:20:36 +04:00
set_fs ( old_fs ) ;
return err ;
}
2006-03-21 09:45:21 +03:00
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 ) ;
}
2005-04-17 02:20:36 +04:00
asmlinkage long compat_sys_setsockopt ( int fd , int level , int optname ,
char __user * optval , int optlen )
{
2006-03-21 09:45:21 +03:00
int err ;
struct socket * sock ;
2006-04-01 14:25:19 +04:00
if ( level = = SOL_IPV6 & & optname = = IPT_SO_SET_REPLACE )
2005-04-17 02:20:36 +04:00
return do_netfilter_replace ( fd , level , optname ,
optval , optlen ) ;
2006-03-21 09:45:21 +03:00
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 ;
2005-04-17 02:20:36 +04:00
}
2006-03-21 09:45:21 +03:00
static int do_get_sock_timeout ( struct socket * sock , int level , int optname ,
2005-04-17 02:20:36 +04:00
char __user * optval , int __user * optlen )
{
struct compat_timeval __user * up ;
struct timeval ktime ;
mm_segment_t old_fs ;
int len , err ;
up = ( struct compat_timeval __user * ) optval ;
if ( get_user ( len , optlen ) )
return - EFAULT ;
if ( len < sizeof ( * up ) )
return - EINVAL ;
len = sizeof ( ktime ) ;
old_fs = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
2006-03-21 09:45:21 +03:00
err = sock_getsockopt ( sock , level , optname , ( char * ) & ktime , & len ) ;
2005-04-17 02:20:36 +04:00
set_fs ( old_fs ) ;
if ( ! err ) {
if ( put_user ( sizeof ( * up ) , optlen ) | |
! access_ok ( VERIFY_WRITE , up , sizeof ( * up ) ) | |
__put_user ( ktime . tv_sec , & up - > tv_sec ) | |
__put_user ( ktime . tv_usec , & up - > tv_usec ) )
err = - EFAULT ;
}
return err ;
}
2006-03-21 09:45:21 +03:00
static int compat_sock_getsockopt ( struct socket * sock , int level , int optname ,
2005-04-17 02:20:36 +04:00
char __user * optval , int __user * optlen )
{
2006-03-21 09:45:21 +03:00
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 ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-22 10:59:39 +03:00
int compat_sock_get_timestamp ( struct sock * sk , struct timeval __user * userstamp )
{
struct compat_timeval __user * ctv =
( struct compat_timeval __user * ) userstamp ;
int err = - ENOENT ;
if ( ! sock_flag ( sk , SOCK_TIMESTAMP ) )
sock_enable_timestamp ( sk ) ;
if ( sk - > sk_stamp . 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 ) )
err = - EFAULT ;
return err ;
}
EXPORT_SYMBOL ( compat_sock_get_timestamp ) ;
2006-03-21 09:45:21 +03:00
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 ;
}
2005-04-17 02:20:36 +04:00
/* 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 ) } ;
# undef AL
asmlinkage long compat_sys_sendmsg ( int fd , struct compat_msghdr __user * msg , unsigned flags )
{
return sys_sendmsg ( fd , ( struct msghdr __user * ) msg , flags | MSG_CMSG_COMPAT ) ;
}
asmlinkage long compat_sys_recvmsg ( int fd , struct compat_msghdr __user * msg , unsigned int flags )
{
return sys_recvmsg ( fd , ( struct msghdr __user * ) msg , flags | MSG_CMSG_COMPAT ) ;
}
asmlinkage long compat_sys_socketcall ( int call , u32 __user * args )
{
int ret ;
u32 a [ 6 ] ;
u32 a0 , a1 ;
2007-02-09 17:25:31 +03:00
2005-04-17 02:20:36 +04:00
if ( call < SYS_SOCKET | | call > SYS_RECVMSG )
return - EINVAL ;
if ( copy_from_user ( a , args , nas [ call ] ) )
return - EFAULT ;
a0 = a [ 0 ] ;
a1 = a [ 1 ] ;
2007-02-09 17:25:31 +03:00
2005-04-17 02:20:36 +04:00
switch ( call ) {
case SYS_SOCKET :
ret = sys_socket ( a0 , a1 , a [ 2 ] ) ;
break ;
case SYS_BIND :
ret = sys_bind ( a0 , compat_ptr ( a1 ) , a [ 2 ] ) ;
break ;
case SYS_CONNECT :
ret = sys_connect ( a0 , compat_ptr ( a1 ) , a [ 2 ] ) ;
break ;
case SYS_LISTEN :
ret = sys_listen ( a0 , a1 ) ;
break ;
case SYS_ACCEPT :
ret = sys_accept ( a0 , compat_ptr ( a1 ) , compat_ptr ( a [ 2 ] ) ) ;
break ;
case SYS_GETSOCKNAME :
ret = sys_getsockname ( a0 , compat_ptr ( a1 ) , compat_ptr ( a [ 2 ] ) ) ;
break ;
case SYS_GETPEERNAME :
ret = sys_getpeername ( a0 , compat_ptr ( a1 ) , compat_ptr ( a [ 2 ] ) ) ;
break ;
case SYS_SOCKETPAIR :
ret = sys_socketpair ( a0 , a1 , a [ 2 ] , compat_ptr ( a [ 3 ] ) ) ;
break ;
case SYS_SEND :
ret = sys_send ( a0 , compat_ptr ( a1 ) , a [ 2 ] , a [ 3 ] ) ;
break ;
case SYS_SENDTO :
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 ] ) ;
break ;
case SYS_RECVFROM :
ret = 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 ) ;
break ;
case SYS_SETSOCKOPT :
ret = compat_sys_setsockopt ( a0 , a1 , a [ 2 ] ,
compat_ptr ( a [ 3 ] ) , a [ 4 ] ) ;
break ;
case SYS_GETSOCKOPT :
ret = compat_sys_getsockopt ( a0 , a1 , a [ 2 ] ,
compat_ptr ( a [ 3 ] ) , compat_ptr ( a [ 4 ] ) ) ;
break ;
case SYS_SENDMSG :
ret = compat_sys_sendmsg ( a0 , compat_ptr ( a1 ) , a [ 2 ] ) ;
break ;
case SYS_RECVMSG :
ret = compat_sys_recvmsg ( a0 , compat_ptr ( a1 ) , a [ 2 ] ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}