2005-09-09 13:04:22 -07:00
/*
* linux / fs / 9 p / trans_fd . c
*
2006-03-25 03:07:23 -08:00
* Fd transport layer . Includes deprecated socket layer .
2005-09-09 13:04:22 -07:00
*
2006-03-25 03:07:23 -08:00
* Copyright ( C ) 2006 by Russ Cox < rsc @ swtch . com >
* Copyright ( C ) 2004 - 2005 by Latchesar Ionkov < lucho @ ionkov . net >
* Copyright ( C ) 2004 - 2005 by Eric Van Hensbergen < ericvh @ gmail . com >
* Copyright ( C ) 1997 - 2002 by Ron Minnich < rminnich @ sarnoff . com >
2005-09-09 13:04:22 -07:00
*
* This program is free software ; you can redistribute it and / or modify
2006-03-25 03:07:28 -08:00
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
2005-09-09 13:04:22 -07:00
*
* 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
*
*/
2006-03-25 03:07:23 -08:00
# include <linux/in.h>
2005-09-09 13:04:22 -07:00
# 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 <linux/file.h>
# include "debug.h"
# include "v9fs.h"
# include "transport.h"
2006-03-25 03:07:23 -08:00
# define V9FS_PORT 564
2005-09-09 13:04:22 -07:00
struct v9fs_trans_fd {
2006-03-25 03:07:23 -08:00
struct file * rd ;
struct file * wr ;
2005-09-09 13:04:22 -07:00
} ;
/**
2006-03-25 03:07:23 -08:00
* v9fs_fd_read - read from a fd
2005-09-09 13:04:22 -07:00
* @ v9ses : session information
* @ v : buffer to receive data into
* @ len : size of receive buffer
*
*/
2006-03-25 03:07:23 -08:00
static int v9fs_fd_read ( struct v9fs_transport * trans , void * v , int len )
2005-09-09 13:04:22 -07:00
{
2006-03-25 03:07:23 -08:00
int ret ;
struct v9fs_trans_fd * ts ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ! trans | | trans - > status = = Disconnected | | ! ( ts = trans - > priv ) )
return - EREMOTEIO ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ! ( ts - > rd - > f_flags & O_NONBLOCK ) )
dprintk ( DEBUG_ERROR , " blocking read ... \n " ) ;
ret = kernel_read ( ts - > rd , ts - > rd - > f_pos , v , len ) ;
if ( ret < = 0 & & ret ! = - ERESTARTSYS & & ret ! = - EAGAIN )
trans - > status = Disconnected ;
return ret ;
2005-09-09 13:04:22 -07:00
}
/**
2006-03-25 03:07:23 -08:00
* v9fs_fd_write - write to a socket
2005-09-09 13:04:22 -07:00
* @ v9ses : session information
* @ v : buffer to send data from
* @ len : size of send buffer
*
*/
2006-03-25 03:07:23 -08:00
static int v9fs_fd_write ( struct v9fs_transport * trans , void * v , int len )
2005-09-09 13:04:22 -07:00
{
2006-03-25 03:07:23 -08:00
int ret ;
mm_segment_t oldfs ;
struct v9fs_trans_fd * ts ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ! trans | | trans - > status = = Disconnected | | ! ( ts = trans - > priv ) )
return - EREMOTEIO ;
if ( ! ( ts - > wr - > f_flags & O_NONBLOCK ) )
dprintk ( DEBUG_ERROR , " blocking write ... \n " ) ;
2005-09-09 13:04:22 -07:00
2006-03-07 21:55:42 -08:00
oldfs = get_fs ( ) ;
2005-09-09 13:04:22 -07:00
set_fs ( get_ds ( ) ) ;
/* The cast to a user pointer is valid due to the set_fs() */
2006-03-25 03:07:23 -08:00
ret = vfs_write ( ts - > wr , ( void __user * ) v , len , & ts - > wr - > f_pos ) ;
2005-09-09 13:04:22 -07:00
set_fs ( oldfs ) ;
2006-03-25 03:07:23 -08:00
if ( ret < = 0 & & ret ! = - ERESTARTSYS & & ret ! = - EAGAIN )
trans - > status = Disconnected ;
2005-09-09 13:04:22 -07:00
return ret ;
}
2006-03-25 03:07:23 -08:00
static unsigned int
v9fs_fd_poll ( struct v9fs_transport * trans , struct poll_table_struct * pt )
2005-09-09 13:04:22 -07:00
{
2006-03-25 03:07:23 -08:00
int ret , n ;
struct v9fs_trans_fd * ts ;
mm_segment_t oldfs ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ! trans | | trans - > status ! = Connected | | ! ( ts = trans - > priv ) )
return - EREMOTEIO ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ! ts - > rd - > f_op | | ! ts - > rd - > f_op - > poll )
return - EIO ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ! ts - > wr - > f_op | | ! ts - > wr - > f_op - > poll )
return - EIO ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
oldfs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
ret = ts - > rd - > f_op - > poll ( ts - > rd , pt ) ;
if ( ret < 0 )
goto end ;
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ts - > rd ! = ts - > wr ) {
n = ts - > wr - > f_op - > poll ( ts - > wr , pt ) ;
if ( n < 0 ) {
ret = n ;
goto end ;
}
ret = ( ret & ~ POLLOUT ) | ( n & ~ POLLIN ) ;
}
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
end :
set_fs ( oldfs ) ;
return ret ;
}
static int v9fs_fd_open ( struct v9fs_session_info * v9ses , int rfd , int wfd )
{
struct v9fs_transport * trans = v9ses - > transport ;
struct v9fs_trans_fd * ts = kmalloc ( sizeof ( struct v9fs_trans_fd ) ,
GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
ts - > rd = fget ( rfd ) ;
ts - > wr = fget ( wfd ) ;
if ( ! ts - > rd | | ! ts - > wr ) {
if ( ts - > rd )
fput ( ts - > rd ) ;
if ( ts - > wr )
fput ( ts - > wr ) ;
2005-09-09 13:04:22 -07:00
kfree ( ts ) ;
return - EIO ;
}
trans - > priv = ts ;
trans - > status = Connected ;
return 0 ;
}
2006-03-25 03:07:23 -08:00
static int v9fs_fd_init ( struct v9fs_session_info * v9ses , const char * addr ,
char * data )
2005-09-09 13:04:22 -07:00
{
2006-03-25 03:07:23 -08:00
if ( v9ses - > rfdno = = ~ 0 | | v9ses - > wfdno = = ~ 0 ) {
printk ( KERN_ERR " v9fs: Insufficient options for proto=fd \n " ) ;
return - ENOPROTOOPT ;
}
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
return v9fs_fd_open ( v9ses , v9ses - > rfdno , v9ses - > wfdno ) ;
}
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
static int v9fs_socket_open ( struct v9fs_session_info * v9ses ,
struct socket * csocket )
{
int fd , ret ;
csocket - > sk - > sk_allocation = GFP_NOIO ;
if ( ( fd = sock_map_fd ( csocket ) ) < 0 ) {
eprintk ( KERN_ERR , " v9fs_socket_open: failed to map fd \n " ) ;
ret = fd ;
release_csocket :
sock_release ( csocket ) ;
return ret ;
}
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
if ( ( ret = v9fs_fd_open ( v9ses , fd , fd ) ) < 0 ) {
sockfd_put ( csocket ) ;
eprintk ( KERN_ERR , " v9fs_socket_open: failed to open fd \n " ) ;
goto release_csocket ;
}
2005-09-09 13:04:22 -07:00
2006-03-25 03:07:23 -08:00
( ( struct v9fs_trans_fd * ) v9ses - > transport - > priv ) - > rd - > f_flags | =
O_NONBLOCK ;
return 0 ;
2005-09-09 13:04:22 -07:00
}
2006-03-25 03:07:23 -08:00
static int v9fs_tcp_init ( struct v9fs_session_info * v9ses , const char * addr ,
char * data )
2006-01-08 01:04:58 -08:00
{
2006-03-25 03:07:23 -08:00
int ret ;
struct socket * csocket = NULL ;
struct sockaddr_in sin_server ;
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 ) ;
if ( ! csocket ) {
eprintk ( KERN_ERR , " v9fs_trans_tcp: problem creating socket \n " ) ;
return - 1 ;
}
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
ret = csocket - > ops - > connect ( csocket ,
( struct sockaddr * ) & sin_server ,
sizeof ( struct sockaddr_in ) , 0 ) ;
if ( ret < 0 ) {
eprintk ( KERN_ERR ,
" v9fs_trans_tcp: problem connecting socket to %s \n " ,
addr ) ;
return ret ;
}
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
return v9fs_socket_open ( v9ses , csocket ) ;
}
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
static int
v9fs_unix_init ( struct v9fs_session_info * v9ses , const char * addr , char * data )
{
int ret ;
struct socket * csocket ;
struct sockaddr_un sun_server ;
if ( strlen ( addr ) > UNIX_PATH_MAX ) {
eprintk ( KERN_ERR , " v9fs_trans_unix: address too long: %s \n " ,
addr ) ;
return - ENAMETOOLONG ;
}
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
sun_server . sun_family = PF_UNIX ;
strcpy ( sun_server . sun_path , addr ) ;
sock_create_kern ( PF_UNIX , SOCK_STREAM , 0 , & csocket ) ;
ret = csocket - > ops - > connect ( csocket , ( struct sockaddr * ) & sun_server ,
sizeof ( struct sockaddr_un ) - 1 , 0 ) ;
if ( ret < 0 ) {
eprintk ( KERN_ERR ,
" v9fs_trans_unix: problem connecting socket: %s: %d \n " ,
addr , ret ) ;
return ret ;
2006-01-08 01:04:58 -08:00
}
2006-03-25 03:07:23 -08:00
return v9fs_socket_open ( v9ses , csocket ) ;
}
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
/**
* v9fs_sock_close - shutdown socket
* @ trans : private socket structure
*
*/
static void v9fs_fd_close ( struct v9fs_transport * trans )
{
struct v9fs_trans_fd * ts ;
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
if ( ! trans )
return ;
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
ts = xchg ( & trans - > priv , NULL ) ;
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
if ( ! ts )
return ;
2006-01-08 01:04:58 -08:00
2006-03-25 03:07:23 -08:00
trans - > status = Disconnected ;
if ( ts - > rd )
fput ( ts - > rd ) ;
if ( ts - > wr )
fput ( ts - > wr ) ;
kfree ( ts ) ;
2006-01-08 01:04:58 -08:00
}
2005-09-09 13:04:22 -07:00
struct v9fs_transport v9fs_trans_fd = {
. init = v9fs_fd_init ,
2006-03-25 03:07:23 -08:00
. write = v9fs_fd_write ,
. read = v9fs_fd_read ,
2005-09-09 13:04:22 -07:00
. close = v9fs_fd_close ,
2006-01-08 01:04:58 -08:00
. poll = v9fs_fd_poll ,
2005-09-09 13:04:22 -07:00
} ;
2006-03-25 03:07:23 -08:00
struct v9fs_transport v9fs_trans_tcp = {
. init = v9fs_tcp_init ,
. write = v9fs_fd_write ,
. read = v9fs_fd_read ,
. close = v9fs_fd_close ,
. poll = v9fs_fd_poll ,
} ;
struct v9fs_transport v9fs_trans_unix = {
. init = v9fs_unix_init ,
. write = v9fs_fd_write ,
. read = v9fs_fd_read ,
. close = v9fs_fd_close ,
. poll = v9fs_fd_poll ,
} ;