2005-09-09 13:04:22 -07:00
/*
* linux / fs / 9 p / trans_socket . c
*
* Socket Transport Layer
*
* Copyright ( C ) 2004 by Eric Van Hensbergen < ericvh @ gmail . com >
* Copyright ( C ) 1997 - 2002 by Ron Minnich < rminnich @ sarnoff . com >
* Copyright ( C ) 1995 , 1996 by Olaf Kirch < okir @ monad . swb . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to :
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02111 - 1301 USA
*
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/net.h>
# include <linux/ipv6.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/un.h>
# include <asm/uaccess.h>
# include <linux/inet.h>
# include <linux/idr.h>
# include "debug.h"
# include "v9fs.h"
# include "transport.h"
# define V9FS_PORT 564
struct v9fs_trans_sock {
struct socket * s ;
} ;
/**
* v9fs_sock_recv - receive from a socket
* @ v9ses : session information
* @ v : buffer to receive data into
* @ len : size of receive buffer
*
*/
static int v9fs_sock_recv ( struct v9fs_transport * trans , void * v , int len )
{
struct msghdr msg ;
struct kvec iov ;
int result ;
mm_segment_t oldfs ;
struct v9fs_trans_sock * ts = trans ? trans - > priv : NULL ;
if ( trans - > status = = Disconnected )
return - EREMOTEIO ;
result = - EINVAL ;
oldfs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
iov . iov_base = v ;
iov . iov_len = len ;
msg . msg_name = NULL ;
msg . msg_namelen = 0 ;
msg . msg_iovlen = 1 ;
msg . msg_control = NULL ;
msg . msg_controllen = 0 ;
msg . msg_namelen = 0 ;
msg . msg_flags = MSG_NOSIGNAL ;
result = kernel_recvmsg ( ts - > s , & msg , & iov , 1 , len , 0 ) ;
dprintk ( DEBUG_TRANS , " socket state %d \n " , ts - > s - > state ) ;
set_fs ( oldfs ) ;
if ( result < = 0 ) {
if ( result ! = - ERESTARTSYS )
trans - > status = Disconnected ;
}
return result ;
}
/**
* v9fs_sock_send - send to a socket
* @ v9ses : session information
* @ v : buffer to send data from
* @ len : size of send buffer
*
*/
static int v9fs_sock_send ( struct v9fs_transport * trans , void * v , int len )
{
struct kvec iov ;
struct msghdr msg ;
int result = - 1 ;
mm_segment_t oldfs ;
struct v9fs_trans_sock * ts = trans ? trans - > priv : NULL ;
dprintk ( DEBUG_TRANS , " Sending packet size %d (%x) \n " , len , len ) ;
dump_data ( v , len ) ;
down ( & trans - > writelock ) ;
oldfs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
iov . iov_base = v ;
iov . iov_len = len ;
msg . msg_name = NULL ;
msg . msg_namelen = 0 ;
msg . msg_iovlen = 1 ;
msg . msg_control = NULL ;
msg . msg_controllen = 0 ;
msg . msg_namelen = 0 ;
msg . msg_flags = MSG_NOSIGNAL ;
result = kernel_sendmsg ( ts - > s , & msg , & iov , 1 , len ) ;
set_fs ( oldfs ) ;
if ( result < 0 ) {
if ( result ! = - ERESTARTSYS )
trans - > status = Disconnected ;
}
up ( & trans - > writelock ) ;
return result ;
}
/**
* v9fs_tcp_init - initialize TCP socket
* @ v9ses : session information
* @ addr : address of server to mount
* @ data : mount options
*
*/
static int
v9fs_tcp_init ( struct v9fs_session_info * v9ses , const char * addr , char * data )
{
struct socket * csocket = NULL ;
struct sockaddr_in sin_server ;
int rc = 0 ;
struct v9fs_trans_sock * ts = NULL ;
struct v9fs_transport * trans = v9ses - > transport ;
sema_init ( & trans - > writelock , 1 ) ;
sema_init ( & trans - > readlock , 1 ) ;
ts = kmalloc ( sizeof ( struct v9fs_trans_sock ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
trans - > priv = ts ;
ts - > s = NULL ;
if ( ! addr )
return - EINVAL ;
dprintk ( DEBUG_TRANS , " Connecting to %s \n " , addr ) ;
sin_server . sin_family = AF_INET ;
sin_server . sin_addr . s_addr = in_aton ( addr ) ;
sin_server . sin_port = htons ( v9ses - > port ) ;
sock_create_kern ( PF_INET , SOCK_STREAM , IPPROTO_TCP , & csocket ) ;
rc = csocket - > ops - > connect ( csocket ,
( struct sockaddr * ) & sin_server ,
sizeof ( struct sockaddr_in ) , 0 ) ;
if ( rc < 0 ) {
eprintk ( KERN_ERR ,
" v9fs_trans_tcp: problem connecting socket to %s \n " ,
addr ) ;
return rc ;
}
csocket - > sk - > sk_allocation = GFP_NOIO ;
ts - > s = csocket ;
trans - > status = Connected ;
return 0 ;
}
/**
* v9fs_unix_init - initialize UNIX domain socket
* @ v9ses : session information
* @ dev_name : path to named pipe
* @ data : mount options
*
*/
static int
v9fs_unix_init ( struct v9fs_session_info * v9ses , const char * dev_name ,
char * data )
{
int rc ;
struct socket * csocket ;
struct sockaddr_un sun_server ;
struct v9fs_transport * trans ;
struct v9fs_trans_sock * ts ;
rc = 0 ;
csocket = NULL ;
trans = v9ses - > transport ;
if ( strlen ( dev_name ) > UNIX_PATH_MAX ) {
eprintk ( KERN_ERR , " v9fs_trans_unix: address too long: %s \n " ,
dev_name ) ;
return - ENOMEM ;
}
ts = kmalloc ( sizeof ( struct v9fs_trans_sock ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
trans - > priv = ts ;
ts - > s = NULL ;
sema_init ( & trans - > writelock , 1 ) ;
sema_init ( & trans - > readlock , 1 ) ;
sun_server . sun_family = PF_UNIX ;
strcpy ( sun_server . sun_path , dev_name ) ;
sock_create_kern ( PF_UNIX , SOCK_STREAM , 0 , & csocket ) ;
rc = csocket - > ops - > connect ( csocket , ( struct sockaddr * ) & sun_server ,
sizeof ( struct sockaddr_un ) - 1 , 0 ) ; /* -1 *is* important */
if ( rc < 0 ) {
eprintk ( KERN_ERR ,
" v9fs_trans_unix: problem connecting socket: %s: %d \n " ,
dev_name , rc ) ;
return rc ;
}
csocket - > sk - > sk_allocation = GFP_NOIO ;
ts - > s = csocket ;
trans - > status = Connected ;
return 0 ;
}
/**
* v9fs_sock_close - shutdown socket
* @ trans : private socket structure
*
*/
static void v9fs_sock_close ( struct v9fs_transport * trans )
{
2005-09-09 13:04:28 -07:00
struct v9fs_trans_sock * ts ;
if ( ! trans )
return ;
ts = trans - > priv ;
2005-09-09 13:04:22 -07:00
if ( ( ts ) & & ( ts - > s ) ) {
dprintk ( DEBUG_TRANS , " closing the socket %p \n " , ts - > s ) ;
sock_release ( ts - > s ) ;
ts - > s = NULL ;
trans - > status = Disconnected ;
dprintk ( DEBUG_TRANS , " socket closed \n " ) ;
}
2005-09-09 13:04:28 -07:00
if ( ts )
kfree ( ts ) ;
trans - > priv = NULL ;
2005-09-09 13:04:22 -07:00
}
struct v9fs_transport v9fs_trans_tcp = {
. init = v9fs_tcp_init ,
. write = v9fs_sock_send ,
. read = v9fs_sock_recv ,
. close = v9fs_sock_close ,
} ;
struct v9fs_transport v9fs_trans_unix = {
. init = v9fs_unix_init ,
. write = v9fs_sock_send ,
. read = v9fs_sock_recv ,
. close = v9fs_sock_close ,
} ;