1
linux/fs/9p/trans_fd.c
Eric Van Hensbergen 42e8c509cf [PATCH] v9fs: update license boilerplate
Update license boilerplate to specify GPLv2 and remove the (at your option
clause).  This change was agreed to by all the copyright holders (approvals
can be found on v9fs-developer mailing list).

Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-25 08:22:54 -08:00

310 lines
6.9 KiB
C

/*
* linux/fs/9p/trans_fd.c
*
* Fd transport layer. Includes deprecated socket layer.
*
* 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>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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/in.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"
#define V9FS_PORT 564
struct v9fs_trans_fd {
struct file *rd;
struct file *wr;
};
/**
* v9fs_fd_read- read from a fd
* @v9ses: session information
* @v: buffer to receive data into
* @len: size of receive buffer
*
*/
static int v9fs_fd_read(struct v9fs_transport *trans, void *v, int len)
{
int ret;
struct v9fs_trans_fd *ts;
if (!trans || trans->status == Disconnected || !(ts = trans->priv))
return -EREMOTEIO;
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;
}
/**
* v9fs_fd_write - write to a socket
* @v9ses: session information
* @v: buffer to send data from
* @len: size of send buffer
*
*/
static int v9fs_fd_write(struct v9fs_transport *trans, void *v, int len)
{
int ret;
mm_segment_t oldfs;
struct v9fs_trans_fd *ts;
if (!trans || trans->status == Disconnected || !(ts = trans->priv))
return -EREMOTEIO;
if (!(ts->wr->f_flags & O_NONBLOCK))
dprintk(DEBUG_ERROR, "blocking write ...\n");
oldfs = get_fs();
set_fs(get_ds());
/* The cast to a user pointer is valid due to the set_fs() */
ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
set_fs(oldfs);
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
trans->status = Disconnected;
return ret;
}
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 || trans->status != Connected || !(ts = trans->priv))
return -EREMOTEIO;
if (!ts->rd->f_op || !ts->rd->f_op->poll)
return -EIO;
if (!ts->wr->f_op || !ts->wr->f_op->poll)
return -EIO;
oldfs = get_fs();
set_fs(get_ds());
ret = ts->rd->f_op->poll(ts->rd, pt);
if (ret < 0)
goto end;
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);
}
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);
kfree(ts);
return -EIO;
}
trans->priv = ts;
trans->status = Connected;
return 0;
}
static int v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr,
char *data)
{
if (v9ses->rfdno == ~0 || v9ses->wfdno == ~0) {
printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
return -ENOPROTOOPT;
}
return v9fs_fd_open(v9ses, v9ses->rfdno, v9ses->wfdno);
}
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;
}
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;
}
((struct v9fs_trans_fd *)v9ses->transport->priv)->rd->f_flags |=
O_NONBLOCK;
return 0;
}
static int v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr,
char *data)
{
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;
}
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;
}
return v9fs_socket_open(v9ses, csocket);
}
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;
}
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;
}
return v9fs_socket_open(v9ses, csocket);
}
/**
* v9fs_sock_close - shutdown socket
* @trans: private socket structure
*
*/
static void v9fs_fd_close(struct v9fs_transport *trans)
{
struct v9fs_trans_fd *ts;
if (!trans)
return;
ts = xchg(&trans->priv, NULL);
if (!ts)
return;
trans->status = Disconnected;
if (ts->rd)
fput(ts->rd);
if (ts->wr)
fput(ts->wr);
kfree(ts);
}
struct v9fs_transport v9fs_trans_fd = {
.init = v9fs_fd_init,
.write = v9fs_fd_write,
.read = v9fs_fd_read,
.close = v9fs_fd_close,
.poll = v9fs_fd_poll,
};
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,
};