2005-04-16 15:20:36 -07:00
/* $Id: timod.c,v 1.19 2002/02/08 03:57:14 davem Exp $
* timod . c : timod emulation .
*
* Copyright ( C ) 1998 Patrik Rak ( prak3264 @ ss1000 . ms . mff . cuni . cz )
*
* Streams & timod emulation based on code
* Copyright ( C ) 1995 , 1996 Mike Jagdis ( jaggy @ purplet . demon . co . uk )
*
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/ioctl.h>
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/netdevice.h>
# include <linux/poll.h>
# include <net/sock.h>
# include <asm/uaccess.h>
# include <asm/termios.h>
# include "conv.h"
# include "socksys.h"
asmlinkage int solaris_ioctl ( unsigned int fd , unsigned int cmd , u32 arg ) ;
static DEFINE_SPINLOCK ( timod_pagelock ) ;
static char * page = NULL ;
# ifndef DEBUG_SOLARIS_KMALLOC
# define mykmalloc kmalloc
# define mykfree kfree
# else
2005-10-21 03:22:24 -04:00
void * mykmalloc ( size_t s , gfp_t gfp )
2005-04-16 15:20:36 -07:00
{
static char * page ;
static size_t free ;
void * r ;
s = ( ( s + 63 ) & ~ 63 ) ;
if ( s > PAGE_SIZE ) {
SOLD ( " too big size, calling real kmalloc " ) ;
return kmalloc ( s , gfp ) ;
}
if ( s > free ) {
/* we are wasting memory, but we don't care */
page = ( char * ) __get_free_page ( gfp ) ;
free = PAGE_SIZE ;
}
r = page ;
page + = s ;
free - = s ;
return r ;
}
void mykfree ( void * p )
{
}
# endif
# ifndef DEBUG_SOLARIS
# define BUF_SIZE PAGE_SIZE
# define PUT_MAGIC(a,m)
# define SCHECK_MAGIC(a,m)
# define BUF_OFFSET 0
# define MKCTL_TRAILER 0
# else
# define BUF_SIZE (PAGE_SIZE-2*sizeof(u64))
# define BUFPAGE_MAGIC 0xBADC0DEDDEADBABEL
# define MKCTL_MAGIC 0xDEADBABEBADC0DEDL
# define PUT_MAGIC(a,m) do{(*(u64*)(a))=(m);}while(0)
# define SCHECK_MAGIC(a,m) do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\
__FILE__ , __LINE__ , __FUNCTION__ , ( m ) , ( a ) ) ; } while ( 0 )
# define BUF_OFFSET sizeof(u64)
# define MKCTL_TRAILER sizeof(u64)
# endif
static char * getpage ( void )
{
char * r ;
SOLD ( " getting page " ) ;
spin_lock ( & timod_pagelock ) ;
if ( page ) {
r = page ;
page = NULL ;
spin_unlock ( & timod_pagelock ) ;
SOLD ( " got cached " ) ;
return r + BUF_OFFSET ;
}
spin_unlock ( & timod_pagelock ) ;
SOLD ( " getting new " ) ;
r = ( char * ) __get_free_page ( GFP_KERNEL ) ;
PUT_MAGIC ( r , BUFPAGE_MAGIC ) ;
PUT_MAGIC ( r + PAGE_SIZE - sizeof ( u64 ) , BUFPAGE_MAGIC ) ;
return r + BUF_OFFSET ;
}
static void putpage ( char * p )
{
SOLD ( " putting page " ) ;
p = p - BUF_OFFSET ;
SCHECK_MAGIC ( p , BUFPAGE_MAGIC ) ;
SCHECK_MAGIC ( p + PAGE_SIZE - sizeof ( u64 ) , BUFPAGE_MAGIC ) ;
spin_lock ( & timod_pagelock ) ;
if ( page ) {
spin_unlock ( & timod_pagelock ) ;
free_page ( ( unsigned long ) p ) ;
SOLD ( " freed it " ) ;
} else {
page = p ;
spin_unlock ( & timod_pagelock ) ;
SOLD ( " cached it " ) ;
}
}
static struct T_primsg * timod_mkctl ( int size )
{
struct T_primsg * it ;
SOLD ( " creating primsg " ) ;
it = ( struct T_primsg * ) mykmalloc ( size + sizeof ( * it ) - sizeof ( s32 ) + 2 * MKCTL_TRAILER , GFP_KERNEL ) ;
if ( it ) {
SOLD ( " got it " ) ;
it - > pri = MSG_HIPRI ;
it - > length = size ;
PUT_MAGIC ( ( char * ) ( ( u64 ) ( ( ( char * ) & it - > type ) + size + 7 ) & ~ 7 ) , MKCTL_MAGIC ) ;
}
return it ;
}
static void timod_wake_socket ( unsigned int fd )
{
struct socket * sock ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
SOLD ( " wakeing socket " ) ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
sock = SOCKET_I ( fdt - > fd [ fd ] - > f_dentry - > d_inode ) ;
2005-04-16 15:20:36 -07:00
wake_up_interruptible ( & sock - > wait ) ;
read_lock ( & sock - > sk - > sk_callback_lock ) ;
if ( sock - > fasync_list & & ! test_bit ( SOCK_ASYNC_WAITDATA , & sock - > flags ) )
__kill_fasync ( sock - > fasync_list , SIGIO , POLL_IN ) ;
read_unlock ( & sock - > sk - > sk_callback_lock ) ;
SOLD ( " done " ) ;
}
static void timod_queue ( unsigned int fd , struct T_primsg * it )
{
struct sol_socket_struct * sock ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
SOLD ( " queuing primsg " ) ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
sock = ( struct sol_socket_struct * ) fdt - > fd [ fd ] - > private_data ;
2005-04-16 15:20:36 -07:00
it - > next = sock - > pfirst ;
sock - > pfirst = it ;
if ( ! sock - > plast )
sock - > plast = it ;
timod_wake_socket ( fd ) ;
SOLD ( " done " ) ;
}
static void timod_queue_end ( unsigned int fd , struct T_primsg * it )
{
struct sol_socket_struct * sock ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
SOLD ( " queuing primsg at end " ) ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
sock = ( struct sol_socket_struct * ) fdt - > fd [ fd ] - > private_data ;
2005-04-16 15:20:36 -07:00
it - > next = NULL ;
if ( sock - > plast )
sock - > plast - > next = it ;
else
sock - > pfirst = it ;
sock - > plast = it ;
SOLD ( " done " ) ;
}
static void timod_error ( unsigned int fd , int prim , int terr , int uerr )
{
struct T_primsg * it ;
SOLD ( " making error " ) ;
it = timod_mkctl ( sizeof ( struct T_error_ack ) ) ;
if ( it ) {
struct T_error_ack * err = ( struct T_error_ack * ) & it - > type ;
SOLD ( " got it " ) ;
err - > PRIM_type = T_ERROR_ACK ;
err - > ERROR_prim = prim ;
err - > TLI_error = terr ;
err - > UNIX_error = uerr ; /* FIXME: convert this */
timod_queue ( fd , it ) ;
}
SOLD ( " done " ) ;
}
static void timod_ok ( unsigned int fd , int prim )
{
struct T_primsg * it ;
struct T_ok_ack * ok ;
SOLD ( " creating ok ack " ) ;
it = timod_mkctl ( sizeof ( * ok ) ) ;
if ( it ) {
SOLD ( " got it " ) ;
ok = ( struct T_ok_ack * ) & it - > type ;
ok - > PRIM_type = T_OK_ACK ;
ok - > CORRECT_prim = prim ;
timod_queue ( fd , it ) ;
}
SOLD ( " done " ) ;
}
static int timod_optmgmt ( unsigned int fd , int flag , char __user * opt_buf , int opt_len , int do_ret )
{
int error , failed ;
int ret_space , ret_len ;
long args [ 5 ] ;
char * ret_pos , * ret_buf ;
int ( * sys_socketcall ) ( int , unsigned long * ) =
( int ( * ) ( int , unsigned long * ) ) SYS ( socketcall ) ;
mm_segment_t old_fs = get_fs ( ) ;
SOLD ( " entry " ) ;
SOLDD ( ( " fd %u flg %u buf %p len %u doret %u " , fd , flag , opt_buf , opt_len , do_ret ) ) ;
if ( ! do_ret & & ( ! opt_buf | | opt_len < = 0 ) )
return 0 ;
SOLD ( " getting page " ) ;
ret_pos = ret_buf = getpage ( ) ;
ret_space = BUF_SIZE ;
ret_len = 0 ;
error = failed = 0 ;
SOLD ( " looping " ) ;
while ( opt_len > = sizeof ( struct opthdr ) ) {
struct opthdr * opt ;
int orig_opt_len ;
SOLD ( " loop start " ) ;
opt = ( struct opthdr * ) ret_pos ;
if ( ret_space < sizeof ( struct opthdr ) ) {
failed = TSYSERR ;
break ;
}
SOLD ( " getting opthdr " ) ;
if ( copy_from_user ( opt , opt_buf , sizeof ( struct opthdr ) ) | |
opt - > len > opt_len ) {
failed = TBADOPT ;
break ;
}
SOLD ( " got opthdr " ) ;
if ( flag = = T_NEGOTIATE ) {
char * buf ;
SOLD ( " handling T_NEGOTIATE " ) ;
buf = ret_pos + sizeof ( struct opthdr ) ;
if ( ret_space < opt - > len + sizeof ( struct opthdr ) | |
copy_from_user ( buf , opt_buf + sizeof ( struct opthdr ) , opt - > len ) ) {
failed = TSYSERR ;
break ;
}
SOLD ( " got optdata " ) ;
args [ 0 ] = fd ;
args [ 1 ] = opt - > level ;
args [ 2 ] = opt - > name ;
args [ 3 ] = ( long ) buf ;
args [ 4 ] = opt - > len ;
SOLD ( " calling SETSOCKOPT " ) ;
set_fs ( KERNEL_DS ) ;
error = sys_socketcall ( SYS_SETSOCKOPT , args ) ;
set_fs ( old_fs ) ;
if ( error ) {
failed = TBADOPT ;
break ;
}
SOLD ( " SETSOCKOPT ok " ) ;
}
orig_opt_len = opt - > len ;
opt - > len = ret_space - sizeof ( struct opthdr ) ;
if ( opt - > len < 0 ) {
failed = TSYSERR ;
break ;
}
args [ 0 ] = fd ;
args [ 1 ] = opt - > level ;
args [ 2 ] = opt - > name ;
args [ 3 ] = ( long ) ( ret_pos + sizeof ( struct opthdr ) ) ;
args [ 4 ] = ( long ) & opt - > len ;
SOLD ( " calling GETSOCKOPT " ) ;
set_fs ( KERNEL_DS ) ;
error = sys_socketcall ( SYS_GETSOCKOPT , args ) ;
set_fs ( old_fs ) ;
if ( error ) {
failed = TBADOPT ;
break ;
}
SOLD ( " GETSOCKOPT ok " ) ;
ret_space - = sizeof ( struct opthdr ) + opt - > len ;
ret_len + = sizeof ( struct opthdr ) + opt - > len ;
ret_pos + = sizeof ( struct opthdr ) + opt - > len ;
opt_len - = sizeof ( struct opthdr ) + orig_opt_len ;
opt_buf + = sizeof ( struct opthdr ) + orig_opt_len ;
SOLD ( " loop end " ) ;
}
SOLD ( " loop done " ) ;
if ( do_ret ) {
SOLD ( " generating ret msg " ) ;
if ( failed )
timod_error ( fd , T_OPTMGMT_REQ , failed , - error ) ;
else {
struct T_primsg * it ;
it = timod_mkctl ( sizeof ( struct T_optmgmt_ack ) + ret_len ) ;
if ( it ) {
struct T_optmgmt_ack * ack =
( struct T_optmgmt_ack * ) & it - > type ;
SOLD ( " got primsg " ) ;
ack - > PRIM_type = T_OPTMGMT_ACK ;
ack - > OPT_length = ret_len ;
ack - > OPT_offset = sizeof ( struct T_optmgmt_ack ) ;
ack - > MGMT_flags = ( failed ? T_FAILURE : flag ) ;
memcpy ( ( ( char * ) ack ) + sizeof ( struct T_optmgmt_ack ) ,
ret_buf , ret_len ) ;
timod_queue ( fd , it ) ;
}
}
}
SOLDD ( ( " put_page %p \n " , ret_buf ) ) ;
putpage ( ret_buf ) ;
SOLD ( " done " ) ;
return 0 ;
}
int timod_putmsg ( unsigned int fd , char __user * ctl_buf , int ctl_len ,
char __user * data_buf , int data_len , int flags )
{
int ret , error , terror ;
char * buf ;
struct file * filp ;
struct inode * ino ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
struct sol_socket_struct * sock ;
mm_segment_t old_fs = get_fs ( ) ;
long args [ 6 ] ;
int ( * sys_socketcall ) ( int , unsigned long __user * ) =
( int ( * ) ( int , unsigned long __user * ) ) SYS ( socketcall ) ;
int ( * sys_sendto ) ( int , void __user * , size_t , unsigned , struct sockaddr __user * , int ) =
( int ( * ) ( int , void __user * , size_t , unsigned , struct sockaddr __user * , int ) ) SYS ( sendto ) ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
filp = fdt - > fd [ fd ] ;
2005-04-16 15:20:36 -07:00
ino = filp - > f_dentry - > d_inode ;
sock = ( struct sol_socket_struct * ) filp - > private_data ;
SOLD ( " entry " ) ;
if ( get_user ( ret , ( int __user * ) A ( ctl_buf ) ) )
return - EFAULT ;
switch ( ret ) {
case T_BIND_REQ :
{
struct T_bind_req req ;
SOLDD ( ( " bind %016lx(%016lx) \n " , sock , filp ) ) ;
SOLD ( " T_BIND_REQ " ) ;
if ( sock - > state ! = TS_UNBND ) {
timod_error ( fd , T_BIND_REQ , TOUTSTATE , 0 ) ;
return 0 ;
}
SOLD ( " state ok " ) ;
if ( copy_from_user ( & req , ctl_buf , sizeof ( req ) ) ) {
timod_error ( fd , T_BIND_REQ , TSYSERR , EFAULT ) ;
return 0 ;
}
SOLD ( " got ctl req " ) ;
if ( req . ADDR_offset & & req . ADDR_length ) {
if ( req . ADDR_length > BUF_SIZE ) {
timod_error ( fd , T_BIND_REQ , TSYSERR , EFAULT ) ;
return 0 ;
}
SOLD ( " req size ok " ) ;
buf = getpage ( ) ;
if ( copy_from_user ( buf , ctl_buf + req . ADDR_offset , req . ADDR_length ) ) {
timod_error ( fd , T_BIND_REQ , TSYSERR , EFAULT ) ;
putpage ( buf ) ;
return 0 ;
}
SOLD ( " got ctl data " ) ;
args [ 0 ] = fd ;
args [ 1 ] = ( long ) buf ;
args [ 2 ] = req . ADDR_length ;
SOLD ( " calling BIND " ) ;
set_fs ( KERNEL_DS ) ;
error = sys_socketcall ( SYS_BIND , args ) ;
set_fs ( old_fs ) ;
putpage ( buf ) ;
SOLD ( " BIND returned " ) ;
} else
error = 0 ;
if ( ! error ) {
struct T_primsg * it ;
if ( req . CONIND_number ) {
args [ 0 ] = fd ;
args [ 1 ] = req . CONIND_number ;
SOLD ( " calling LISTEN " ) ;
set_fs ( KERNEL_DS ) ;
error = sys_socketcall ( SYS_LISTEN , args ) ;
set_fs ( old_fs ) ;
SOLD ( " LISTEN done " ) ;
}
it = timod_mkctl ( sizeof ( struct T_bind_ack ) + sizeof ( struct sockaddr ) ) ;
if ( it ) {
struct T_bind_ack * ack ;
ack = ( struct T_bind_ack * ) & it - > type ;
ack - > PRIM_type = T_BIND_ACK ;
ack - > ADDR_offset = sizeof ( * ack ) ;
ack - > ADDR_length = sizeof ( struct sockaddr ) ;
ack - > CONIND_number = req . CONIND_number ;
args [ 0 ] = fd ;
args [ 1 ] = ( long ) ( ack + sizeof ( * ack ) ) ;
args [ 2 ] = ( long ) & ack - > ADDR_length ;
set_fs ( KERNEL_DS ) ;
sys_socketcall ( SYS_GETSOCKNAME , args ) ;
set_fs ( old_fs ) ;
sock - > state = TS_IDLE ;
timod_ok ( fd , T_BIND_REQ ) ;
timod_queue_end ( fd , it ) ;
SOLD ( " BIND done " ) ;
return 0 ;
}
}
SOLD ( " some error " ) ;
switch ( error ) {
case - EINVAL :
terror = TOUTSTATE ;
error = 0 ;
break ;
case - EACCES :
terror = TACCES ;
error = 0 ;
break ;
case - EADDRNOTAVAIL :
case - EADDRINUSE :
terror = TNOADDR ;
error = 0 ;
break ;
default :
terror = TSYSERR ;
break ;
}
timod_error ( fd , T_BIND_REQ , terror , - error ) ;
SOLD ( " BIND done " ) ;
return 0 ;
}
case T_CONN_REQ :
{
struct T_conn_req req ;
unsigned short oldflags ;
struct T_primsg * it ;
SOLD ( " T_CONN_REQ " ) ;
if ( sock - > state ! = TS_UNBND & & sock - > state ! = TS_IDLE ) {
timod_error ( fd , T_CONN_REQ , TOUTSTATE , 0 ) ;
return 0 ;
}
SOLD ( " state ok " ) ;
if ( copy_from_user ( & req , ctl_buf , sizeof ( req ) ) ) {
timod_error ( fd , T_CONN_REQ , TSYSERR , EFAULT ) ;
return 0 ;
}
SOLD ( " got ctl req " ) ;
if ( ctl_len > BUF_SIZE ) {
timod_error ( fd , T_CONN_REQ , TSYSERR , EFAULT ) ;
return 0 ;
}
SOLD ( " req size ok " ) ;
buf = getpage ( ) ;
if ( copy_from_user ( buf , ctl_buf , ctl_len ) ) {
timod_error ( fd , T_CONN_REQ , TSYSERR , EFAULT ) ;
putpage ( buf ) ;
return 0 ;
}
# ifdef DEBUG_SOLARIS
{
char * ptr = buf ;
int len = ctl_len ;
printk ( " returned data (%d bytes): " , len ) ;
while ( len - - ) {
if ( ! ( len & 7 ) )
printk ( " " ) ;
printk ( " %02x " , ( unsigned char ) * ptr + + ) ;
}
printk ( " \n " ) ;
}
# endif
SOLD ( " got ctl data " ) ;
args [ 0 ] = fd ;
args [ 1 ] = ( long ) buf + req . DEST_offset ;
args [ 2 ] = req . DEST_length ;
oldflags = filp - > f_flags ;
filp - > f_flags & = ~ O_NONBLOCK ;
SOLD ( " calling CONNECT " ) ;
set_fs ( KERNEL_DS ) ;
error = sys_socketcall ( SYS_CONNECT , args ) ;
set_fs ( old_fs ) ;
filp - > f_flags = oldflags ;
SOLD ( " CONNECT done " ) ;
if ( ! error ) {
struct T_conn_con * con ;
SOLD ( " no error " ) ;
it = timod_mkctl ( ctl_len ) ;
if ( ! it ) {
putpage ( buf ) ;
return - ENOMEM ;
}
con = ( struct T_conn_con * ) & it - > type ;
# ifdef DEBUG_SOLARIS
{
char * ptr = buf ;
int len = ctl_len ;
printk ( " returned data (%d bytes): " , len ) ;
while ( len - - ) {
if ( ! ( len & 7 ) )
printk ( " " ) ;
printk ( " %02x " , ( unsigned char ) * ptr + + ) ;
}
printk ( " \n " ) ;
}
# endif
memcpy ( con , buf , ctl_len ) ;
SOLD ( " copied ctl_buf " ) ;
con - > PRIM_type = T_CONN_CON ;
sock - > state = TS_DATA_XFER ;
} else {
struct T_discon_ind * dis ;
SOLD ( " some error " ) ;
it = timod_mkctl ( sizeof ( * dis ) ) ;
if ( ! it ) {
putpage ( buf ) ;
return - ENOMEM ;
}
SOLD ( " got primsg " ) ;
dis = ( struct T_discon_ind * ) & it - > type ;
dis - > PRIM_type = T_DISCON_IND ;
dis - > DISCON_reason = - error ; /* FIXME: convert this as in iABI_errors() */
dis - > SEQ_number = 0 ;
}
putpage ( buf ) ;
timod_ok ( fd , T_CONN_REQ ) ;
it - > pri = 0 ;
timod_queue_end ( fd , it ) ;
SOLD ( " CONNECT done " ) ;
return 0 ;
}
case T_OPTMGMT_REQ :
{
struct T_optmgmt_req req ;
SOLD ( " OPTMGMT_REQ " ) ;
if ( copy_from_user ( & req , ctl_buf , sizeof ( req ) ) )
return - EFAULT ;
SOLD ( " got req " ) ;
return timod_optmgmt ( fd , req . MGMT_flags ,
req . OPT_offset > 0 ? ctl_buf + req . OPT_offset : NULL ,
req . OPT_length , 1 ) ;
}
case T_UNITDATA_REQ :
{
struct T_unitdata_req req ;
int err ;
SOLD ( " T_UNITDATA_REQ " ) ;
if ( sock - > state ! = TS_IDLE & & sock - > state ! = TS_DATA_XFER ) {
timod_error ( fd , T_CONN_REQ , TOUTSTATE , 0 ) ;
return 0 ;
}
SOLD ( " state ok " ) ;
if ( copy_from_user ( & req , ctl_buf , sizeof ( req ) ) ) {
timod_error ( fd , T_CONN_REQ , TSYSERR , EFAULT ) ;
return 0 ;
}
SOLD ( " got ctl req " ) ;
# ifdef DEBUG_SOLARIS
{
char * ptr = ctl_buf + req . DEST_offset ;
int len = req . DEST_length ;
printk ( " socket address (%d bytes): " , len ) ;
while ( len - - ) {
char c ;
if ( get_user ( c , ptr ) )
printk ( " ?? " ) ;
else
printk ( " %02x " , ( unsigned char ) c ) ;
ptr + + ;
}
printk ( " \n " ) ;
}
# endif
err = sys_sendto ( fd , data_buf , data_len , 0 , req . DEST_length > 0 ? ( struct sockaddr __user * ) ( ctl_buf + req . DEST_offset ) : NULL , req . DEST_length ) ;
if ( err = = data_len )
return 0 ;
if ( err > = 0 ) {
printk ( " timod: sendto failed to send all the data \n " ) ;
return 0 ;
}
timod_error ( fd , T_CONN_REQ , TSYSERR , - err ) ;
return 0 ;
}
default :
printk ( KERN_INFO " timod_putmsg: unsupported command %u. \n " , ret ) ;
break ;
}
return - EINVAL ;
}
int timod_getmsg ( unsigned int fd , char __user * ctl_buf , int ctl_maxlen , s32 __user * ctl_len ,
char __user * data_buf , int data_maxlen , s32 __user * data_len , int * flags_p )
{
int error ;
int oldflags ;
struct file * filp ;
struct inode * ino ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
struct sol_socket_struct * sock ;
struct T_unitdata_ind udi ;
mm_segment_t old_fs = get_fs ( ) ;
long args [ 6 ] ;
char __user * tmpbuf ;
int tmplen ;
int ( * sys_socketcall ) ( int , unsigned long __user * ) =
( int ( * ) ( int , unsigned long __user * ) ) SYS ( socketcall ) ;
int ( * sys_recvfrom ) ( int , void __user * , size_t , unsigned , struct sockaddr __user * , int __user * ) ;
SOLD ( " entry " ) ;
SOLDD ( ( " %u %p %d %p %p %d %p %d \n " , fd , ctl_buf , ctl_maxlen , ctl_len , data_buf , data_maxlen , data_len , * flags_p ) ) ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
filp = fdt - > fd [ fd ] ;
2005-04-16 15:20:36 -07:00
ino = filp - > f_dentry - > d_inode ;
sock = ( struct sol_socket_struct * ) filp - > private_data ;
SOLDD ( ( " %p %p \n " , sock - > pfirst , sock - > pfirst ? sock - > pfirst - > next : NULL ) ) ;
if ( ctl_maxlen > 0 & & ! sock - > pfirst & & SOCKET_I ( ino ) - > type = = SOCK_STREAM
& & sock - > state = = TS_IDLE ) {
SOLD ( " calling LISTEN " ) ;
args [ 0 ] = fd ;
args [ 1 ] = - 1 ;
set_fs ( KERNEL_DS ) ;
sys_socketcall ( SYS_LISTEN , args ) ;
set_fs ( old_fs ) ;
SOLD ( " LISTEN done " ) ;
}
if ( ! ( filp - > f_flags & O_NONBLOCK ) ) {
struct poll_wqueues wait_table ;
poll_table * wait ;
poll_initwait ( & wait_table ) ;
wait = & wait_table . pt ;
for ( ; ; ) {
SOLD ( " loop " ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
/* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */
/* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */
/* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */
/* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */
/* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */
/* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */
if ( ctl_maxlen > = 0 & & sock - > pfirst & & ( * flags_p ! = MSG_HIPRI | | sock - > pfirst - > pri = = MSG_HIPRI ) )
break ;
SOLD ( " cond 1 passed " ) ;
if (
# if 1
* flags_p ! = MSG_HIPRI & &
# endif
( ( filp - > f_op - > poll ( filp , wait ) & POLLIN ) | |
( filp - > f_op - > poll ( filp , NULL ) & POLLIN ) | |
signal_pending ( current ) )
) {
break ;
}
if ( * flags_p = = MSG_HIPRI ) {
SOLD ( " avoiding lockup " ) ;
break ;
}
if ( wait_table . error ) {
SOLD ( " wait-table error " ) ;
poll_freewait ( & wait_table ) ;
return wait_table . error ;
}
SOLD ( " scheduling " ) ;
schedule ( ) ;
}
SOLD ( " loop done " ) ;
current - > state = TASK_RUNNING ;
poll_freewait ( & wait_table ) ;
if ( signal_pending ( current ) ) {
SOLD ( " signal pending " ) ;
return - EINTR ;
}
}
if ( ctl_maxlen > = 0 & & sock - > pfirst ) {
struct T_primsg * it = sock - > pfirst ;
int l = min_t ( int , ctl_maxlen , it - > length ) ;
SCHECK_MAGIC ( ( char * ) ( ( u64 ) ( ( ( char * ) & it - > type ) + sock - > offset + it - > length + 7 ) & ~ 7 ) , MKCTL_MAGIC ) ;
SOLD ( " purting ctl data " ) ;
if ( copy_to_user ( ctl_buf ,
( char * ) & it - > type + sock - > offset , l ) )
return - EFAULT ;
SOLD ( " pur it " ) ;
if ( put_user ( l , ctl_len ) )
return - EFAULT ;
SOLD ( " set ctl_len " ) ;
* flags_p = it - > pri ;
it - > length - = l ;
if ( it - > length ) {
SOLD ( " more ctl " ) ;
sock - > offset + = l ;
return MORECTL ;
} else {
SOLD ( " removing message " ) ;
sock - > pfirst = it - > next ;
if ( ! sock - > pfirst )
sock - > plast = NULL ;
SOLDD ( ( " getmsg kfree %016lx->%016lx \n " , it , sock - > pfirst ) ) ;
mykfree ( it ) ;
sock - > offset = 0 ;
SOLD ( " ctl done " ) ;
return 0 ;
}
}
* flags_p = 0 ;
if ( ctl_maxlen > = 0 ) {
SOLD ( " ACCEPT perhaps? " ) ;
if ( SOCKET_I ( ino ) - > type = = SOCK_STREAM & & sock - > state = = TS_IDLE ) {
struct T_conn_ind ind ;
char * buf = getpage ( ) ;
int len = BUF_SIZE ;
SOLD ( " trying ACCEPT " ) ;
if ( put_user ( ctl_maxlen - sizeof ( ind ) , ctl_len ) )
return - EFAULT ;
args [ 0 ] = fd ;
args [ 1 ] = ( long ) buf ;
args [ 2 ] = ( long ) & len ;
oldflags = filp - > f_flags ;
filp - > f_flags | = O_NONBLOCK ;
SOLD ( " calling ACCEPT " ) ;
set_fs ( KERNEL_DS ) ;
error = sys_socketcall ( SYS_ACCEPT , args ) ;
set_fs ( old_fs ) ;
filp - > f_flags = oldflags ;
if ( error < 0 ) {
SOLD ( " some error " ) ;
putpage ( buf ) ;
return error ;
}
if ( error ) {
SOLD ( " connect " ) ;
putpage ( buf ) ;
if ( sizeof ( ind ) > ctl_maxlen ) {
SOLD ( " generating CONN_IND " ) ;
ind . PRIM_type = T_CONN_IND ;
ind . SRC_length = len ;
ind . SRC_offset = sizeof ( ind ) ;
ind . OPT_length = ind . OPT_offset = 0 ;
ind . SEQ_number = error ;
if ( copy_to_user ( ctl_buf , & ind , sizeof ( ind ) ) | |
put_user ( sizeof ( ind ) + ind . SRC_length , ctl_len ) )
return - EFAULT ;
SOLD ( " CONN_IND created " ) ;
}
if ( data_maxlen > = 0 )
put_user ( 0 , data_len ) ;
SOLD ( " CONN_IND done " ) ;
return 0 ;
}
if ( len > ctl_maxlen ) {
SOLD ( " data don't fit " ) ;
putpage ( buf ) ;
return - EFAULT ; /* XXX - is this ok ? */
}
if ( copy_to_user ( ctl_buf , buf , len ) | | put_user ( len , ctl_len ) ) {
SOLD ( " can't copy data " ) ;
putpage ( buf ) ;
return - EFAULT ;
}
SOLD ( " ACCEPT done " ) ;
putpage ( buf ) ;
}
}
SOLD ( " checking data req " ) ;
if ( data_maxlen < = 0 ) {
if ( data_maxlen = = 0 )
put_user ( 0 , data_len ) ;
if ( ctl_maxlen > = 0 )
put_user ( 0 , ctl_len ) ;
return - EAGAIN ;
}
SOLD ( " wants data " ) ;
if ( ctl_maxlen > sizeof ( udi ) & & sock - > state = = TS_IDLE ) {
SOLD ( " udi fits " ) ;
tmpbuf = ctl_buf + sizeof ( udi ) ;
tmplen = ctl_maxlen - sizeof ( udi ) ;
} else {
SOLD ( " udi does not fit " ) ;
tmpbuf = NULL ;
tmplen = 0 ;
}
if ( put_user ( tmplen , ctl_len ) )
return - EFAULT ;
SOLD ( " set ctl_len " ) ;
oldflags = filp - > f_flags ;
filp - > f_flags | = O_NONBLOCK ;
SOLD ( " calling recvfrom " ) ;
sys_recvfrom = ( int ( * ) ( int , void __user * , size_t , unsigned , struct sockaddr __user * , int __user * ) ) SYS ( recvfrom ) ;
error = sys_recvfrom ( fd , data_buf , data_maxlen , 0 , ( struct sockaddr __user * ) tmpbuf , ctl_len ) ;
filp - > f_flags = oldflags ;
if ( error < 0 )
return error ;
SOLD ( " error >= 0 " ) ;
if ( error & & ctl_maxlen > sizeof ( udi ) & & sock - > state = = TS_IDLE ) {
SOLD ( " generating udi " ) ;
udi . PRIM_type = T_UNITDATA_IND ;
if ( get_user ( udi . SRC_length , ctl_len ) )
return - EFAULT ;
udi . SRC_offset = sizeof ( udi ) ;
udi . OPT_length = udi . OPT_offset = 0 ;
if ( copy_to_user ( ctl_buf , & udi , sizeof ( udi ) ) | |
put_user ( sizeof ( udi ) + udi . SRC_length , ctl_len ) )
return - EFAULT ;
SOLD ( " udi done " ) ;
} else {
if ( put_user ( 0 , ctl_len ) )
return - EFAULT ;
}
put_user ( error , data_len ) ;
SOLD ( " done " ) ;
return 0 ;
}
asmlinkage int solaris_getmsg ( unsigned int fd , u32 arg1 , u32 arg2 , u32 arg3 )
{
struct file * filp ;
struct inode * ino ;
struct strbuf __user * ctlptr ;
struct strbuf __user * datptr ;
struct strbuf ctl , dat ;
int __user * flgptr ;
int flags ;
int error = - EBADF ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
SOLD ( " entry " ) ;
lock_kernel ( ) ;
if ( fd > = NR_OPEN ) goto out ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
filp = fdt - > fd [ fd ] ;
2005-04-16 15:20:36 -07:00
if ( ! filp ) goto out ;
ino = filp - > f_dentry - > d_inode ;
if ( ! ino | | ! S_ISSOCK ( ino - > i_mode ) )
goto out ;
ctlptr = ( struct strbuf __user * ) A ( arg1 ) ;
datptr = ( struct strbuf __user * ) A ( arg2 ) ;
flgptr = ( int __user * ) A ( arg3 ) ;
error = - EFAULT ;
if ( ctlptr ) {
if ( copy_from_user ( & ctl , ctlptr , sizeof ( struct strbuf ) ) | |
put_user ( - 1 , & ctlptr - > len ) )
goto out ;
} else
ctl . maxlen = - 1 ;
if ( datptr ) {
if ( copy_from_user ( & dat , datptr , sizeof ( struct strbuf ) ) | |
put_user ( - 1 , & datptr - > len ) )
goto out ;
} else
dat . maxlen = - 1 ;
if ( get_user ( flags , flgptr ) )
goto out ;
switch ( flags ) {
case 0 :
case MSG_HIPRI :
case MSG_ANY :
case MSG_BAND :
break ;
default :
error = - EINVAL ;
goto out ;
}
error = timod_getmsg ( fd , A ( ctl . buf ) , ctl . maxlen , & ctlptr - > len ,
A ( dat . buf ) , dat . maxlen , & datptr - > len , & flags ) ;
if ( ! error & & put_user ( flags , flgptr ) )
error = - EFAULT ;
out :
unlock_kernel ( ) ;
SOLD ( " done " ) ;
return error ;
}
asmlinkage int solaris_putmsg ( unsigned int fd , u32 arg1 , u32 arg2 , u32 arg3 )
{
struct file * filp ;
struct inode * ino ;
struct strbuf __user * ctlptr ;
struct strbuf __user * datptr ;
struct strbuf ctl , dat ;
int flags = ( int ) arg3 ;
int error = - EBADF ;
2005-09-09 13:04:12 -07:00
struct fdtable * fdt ;
2005-04-16 15:20:36 -07:00
SOLD ( " entry " ) ;
lock_kernel ( ) ;
if ( fd > = NR_OPEN ) goto out ;
2005-09-09 13:04:12 -07:00
fdt = files_fdtable ( current - > files ) ;
filp = fdt - > fd [ fd ] ;
2005-04-16 15:20:36 -07:00
if ( ! filp ) goto out ;
ino = filp - > f_dentry - > d_inode ;
if ( ! ino ) goto out ;
if ( ! S_ISSOCK ( ino - > i_mode ) & &
( imajor ( ino ) ! = 30 | | iminor ( ino ) ! = 1 ) )
goto out ;
ctlptr = A ( arg1 ) ;
datptr = A ( arg2 ) ;
error = - EFAULT ;
if ( ctlptr ) {
if ( copy_from_user ( & ctl , ctlptr , sizeof ( ctl ) ) )
goto out ;
if ( ctl . len < 0 & & flags ) {
error = - EINVAL ;
goto out ;
}
} else {
ctl . len = 0 ;
ctl . buf = 0 ;
}
if ( datptr ) {
if ( copy_from_user ( & dat , datptr , sizeof ( dat ) ) )
goto out ;
} else {
dat . len = 0 ;
dat . buf = 0 ;
}
error = timod_putmsg ( fd , A ( ctl . buf ) , ctl . len ,
A ( dat . buf ) , dat . len , flags ) ;
out :
unlock_kernel ( ) ;
SOLD ( " done " ) ;
return error ;
}