diff options
Diffstat (limited to 'fs/9p/trans_fd.c')
-rw-r--r-- | fs/9p/trans_fd.c | 297 |
1 files changed, 196 insertions, 101 deletions
diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c index 5b2ce21b10f..8029844d6b2 100644 --- a/fs/9p/trans_fd.c +++ b/fs/9p/trans_fd.c @@ -1,10 +1,13 @@ /* * linux/fs/9p/trans_fd.c * - * File Descriptor Transport Layer + * Fd transport layer. Includes deprecated socket layer. * - * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> + * 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> + * 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 @@ -25,6 +28,7 @@ */ #include <linux/config.h> +#include <linux/in.h> #include <linux/module.h> #include <linux/net.h> #include <linux/ipv6.h> @@ -40,89 +44,119 @@ #include "v9fs.h" #include "transport.h" +#define V9FS_PORT 564 + struct v9fs_trans_fd { - struct file *in_file; - struct file *out_file; + struct file *rd; + struct file *wr; }; /** - * v9fs_fd_recv - receive from a socket + * 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_recv(struct v9fs_transport *trans, void *v, int len) +static int v9fs_fd_read(struct v9fs_transport *trans, void *v, int len) { - struct v9fs_trans_fd *ts = trans ? trans->priv : NULL; + int ret; + struct v9fs_trans_fd *ts; - if (!trans || trans->status != Connected || !ts) - return -EIO; + if (!trans || trans->status == Disconnected || !(ts = trans->priv)) + return -EREMOTEIO; - return kernel_read(ts->in_file, ts->in_file->f_pos, v, len); + 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_send - send to a socket + * 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_send(struct v9fs_transport *trans, void *v, int len) +static int v9fs_fd_write(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; + int ret; + mm_segment_t oldfs; + struct v9fs_trans_fd *ts; - if (!trans || trans->status != Connected || !ts) - return -EIO; + 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->out_file, (void __user *)v, len, &ts->out_file->f_pos); + 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; } -/** - * 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) +static unsigned int +v9fs_fd_poll(struct v9fs_transport *trans, struct poll_table_struct *pt) { - struct v9fs_trans_fd *ts = NULL; - struct v9fs_transport *trans = v9ses->transport; + int ret, n; + struct v9fs_trans_fd *ts; + mm_segment_t oldfs; - if((v9ses->wfdno == ~0) || (v9ses->rfdno == ~0)) { - printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); - return -ENOPROTOOPT; - } + if (!trans || trans->status != Connected || !(ts = trans->priv)) + return -EREMOTEIO; - ts = kmalloc(sizeof(struct v9fs_trans_fd), GFP_KERNEL); + if (!ts->rd->f_op || !ts->rd->f_op->poll) + return -EIO; - if (!ts) - return -ENOMEM; + if (!ts->wr->f_op || !ts->wr->f_op->poll) + return -EIO; - ts->in_file = fget( v9ses->rfdno ); - ts->out_file = fget( v9ses->wfdno ); + oldfs = get_fs(); + set_fs(get_ds()); - if (!ts->in_file || !ts->out_file) { - if (ts->in_file) - fput(ts->in_file); + ret = ts->rd->f_op->poll(ts->rd, pt); + if (ret < 0) + goto end; - if (ts->out_file) - fput(ts->out_file); + 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; } @@ -133,84 +167,145 @@ v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, char *data) return 0; } - -/** - * v9fs_fd_close - shutdown file descriptor - * @trans: private socket structure - * - */ - -static void v9fs_fd_close(struct v9fs_transport *trans) +static int v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, + char *data) { - struct v9fs_trans_fd *ts; - - if (!trans) - return; - - ts = xchg(&trans->priv, NULL); + if (v9ses->rfdno == ~0 || v9ses->wfdno == ~0) { + printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); + return -ENOPROTOOPT; + } - if (!ts) - return; + return v9fs_fd_open(v9ses, v9ses->rfdno, v9ses->wfdno); +} - trans->status = Disconnected; - if (ts->in_file) - fput(ts->in_file); +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 (ts->out_file) - fput(ts->out_file); + 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; + } - kfree(ts); + ((struct v9fs_trans_fd *)v9ses->transport->priv)->rd->f_flags |= + O_NONBLOCK; + return 0; } -static unsigned int -v9fs_fd_poll(struct v9fs_transport *trans, struct poll_table_struct *pt) +static int v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, + char *data) { - int ret, n; - struct v9fs_trans_fd *ts; - mm_segment_t oldfs; + 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; + } - if (!trans) - return -EIO; + 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; + } - ts = trans->priv; - if (trans->status != Connected || !ts) - return -EIO; + return v9fs_socket_open(v9ses, csocket); +} - oldfs = get_fs(); - set_fs(get_ds()); +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; + } - if (!ts->in_file->f_op || !ts->in_file->f_op->poll) { - ret = -EIO; - goto end; + 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; } - ret = ts->in_file->f_op->poll(ts->in_file, pt); + return v9fs_socket_open(v9ses, csocket); +} - if (ts->out_file != ts->in_file) { - if (!ts->out_file->f_op || !ts->out_file->f_op->poll) { - ret = -EIO; - goto end; - } +/** + * v9fs_sock_close - shutdown socket + * @trans: private socket structure + * + */ +static void v9fs_fd_close(struct v9fs_transport *trans) +{ + struct v9fs_trans_fd *ts; - n = ts->out_file->f_op->poll(ts->out_file, pt); + if (!trans) + return; - ret &= ~POLLOUT; - n &= ~POLLIN; + ts = xchg(&trans->priv, NULL); - ret |= n; - } + if (!ts) + return; -end: - set_fs(oldfs); - return ret; + 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_send, - .read = v9fs_fd_recv, + .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, +}; |