2005-09-10 00:04:22 +04:00
/*
* linux / fs / 9 p / trans_fd . c
*
* File Descriptor Transport Layer
*
2006-01-08 12:04:58 +03:00
* Copyright ( C ) 2005 by Latchesar Ionkov < lucho @ ionkov . net >
2005-09-10 00:04:22 +04:00
* Copyright ( C ) 2005 by Eric Van Hensbergen < ericvh @ gmail . com >
*
* 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 <linux/file.h>
# include "debug.h"
# include "v9fs.h"
# include "transport.h"
struct v9fs_trans_fd {
struct file * in_file ;
struct file * out_file ;
} ;
/**
* v9fs_fd_recv - receive from a socket
* @ v9ses : session information
* @ v : buffer to receive data into
* @ len : size of receive buffer
*
*/
static int v9fs_fd_recv ( struct v9fs_transport * trans , void * v , int len )
{
struct v9fs_trans_fd * ts = trans ? trans - > priv : NULL ;
if ( ! trans | | trans - > status ! = Connected | | ! ts )
return - EIO ;
return kernel_read ( ts - > in_file , ts - > in_file - > f_pos , v , len ) ;
}
/**
* v9fs_fd_send - send to a socket
* @ v9ses : session information
* @ v : buffer to send data from
* @ len : size of send buffer
*
*/
static int v9fs_fd_send ( struct v9fs_transport * trans , void * v , int len )
{
struct v9fs_trans_fd * ts = trans ? trans - > priv : NULL ;
mm_segment_t oldfs = get_fs ( ) ;
int ret = 0 ;
if ( ! trans | | trans - > status ! = Connected | | ! ts )
return - EIO ;
2006-03-08 08:55:42 +03:00
oldfs = get_fs ( ) ;
2005-09-10 00:04:22 +04:00
set_fs ( get_ds ( ) ) ;
/* The cast to a user pointer is valid due to the set_fs() */
ret = vfs_write ( ts - > out_file , ( void __user * ) v , len , & ts - > out_file - > f_pos ) ;
set_fs ( oldfs ) ;
return ret ;
}
/**
* v9fs_fd_init - initialize file descriptor transport
* @ v9ses : session information
* @ addr : address of server to mount
* @ data : mount options
*
*/
static int
v9fs_fd_init ( struct v9fs_session_info * v9ses , const char * addr , char * data )
{
struct v9fs_trans_fd * ts = NULL ;
struct v9fs_transport * trans = v9ses - > transport ;
if ( ( v9ses - > wfdno = = ~ 0 ) | | ( v9ses - > rfdno = = ~ 0 ) ) {
printk ( KERN_ERR " v9fs: Insufficient options for proto=fd \n " ) ;
return - ENOPROTOOPT ;
}
ts = kmalloc ( sizeof ( struct v9fs_trans_fd ) , GFP_KERNEL ) ;
if ( ! ts )
return - ENOMEM ;
ts - > in_file = fget ( v9ses - > rfdno ) ;
ts - > out_file = fget ( v9ses - > wfdno ) ;
if ( ! ts - > in_file | | ! ts - > out_file ) {
if ( ts - > in_file )
fput ( ts - > in_file ) ;
if ( ts - > out_file )
fput ( ts - > out_file ) ;
kfree ( ts ) ;
return - EIO ;
}
trans - > priv = ts ;
trans - > status = Connected ;
return 0 ;
}
/**
* v9fs_fd_close - shutdown file descriptor
* @ trans : private socket structure
*
*/
static void v9fs_fd_close ( struct v9fs_transport * trans )
{
struct v9fs_trans_fd * ts ;
if ( ! trans )
return ;
2006-01-08 12:04:56 +03:00
ts = xchg ( & trans - > priv , NULL ) ;
2005-09-10 00:04:22 +04:00
if ( ! ts )
return ;
2006-01-08 12:04:56 +03:00
trans - > status = Disconnected ;
2005-09-10 00:04:22 +04:00
if ( ts - > in_file )
fput ( ts - > in_file ) ;
if ( ts - > out_file )
fput ( ts - > out_file ) ;
kfree ( ts ) ;
}
2006-01-08 12:04:58 +03:00
static unsigned int
v9fs_fd_poll ( struct v9fs_transport * trans , struct poll_table_struct * pt )
{
int ret , n ;
struct v9fs_trans_fd * ts ;
mm_segment_t oldfs ;
if ( ! trans )
return - EIO ;
ts = trans - > priv ;
if ( trans - > status ! = Connected | | ! ts )
return - EIO ;
oldfs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
if ( ! ts - > in_file - > f_op | | ! ts - > in_file - > f_op - > poll ) {
ret = - EIO ;
goto end ;
}
ret = ts - > in_file - > f_op - > poll ( ts - > in_file , pt ) ;
if ( ts - > out_file ! = ts - > in_file ) {
if ( ! ts - > out_file - > f_op | | ! ts - > out_file - > f_op - > poll ) {
ret = - EIO ;
goto end ;
}
n = ts - > out_file - > f_op - > poll ( ts - > out_file , pt ) ;
ret & = ~ POLLOUT ;
n & = ~ POLLIN ;
ret | = n ;
}
end :
set_fs ( oldfs ) ;
return ret ;
}
2005-09-10 00:04:22 +04:00
struct v9fs_transport v9fs_trans_fd = {
. init = v9fs_fd_init ,
. write = v9fs_fd_send ,
. read = v9fs_fd_recv ,
. close = v9fs_fd_close ,
2006-01-08 12:04:58 +03:00
. poll = v9fs_fd_poll ,
2005-09-10 00:04:22 +04:00
} ;