1/* 2 * linux/fs/9p/trans_fd.c 3 * 4 * Fd transport layer. Includes deprecated socket layer. 5 * 6 * Copyright (C) 2006 by Russ Cox <rsc@swtch.com> 7 * Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> 8 * Copyright (C) 2004-2005 by Eric Van Hensbergen <ericvh@gmail.com> 9 * Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 13 * as published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to: 22 * Free Software Foundation 23 * 51 Franklin Street, Fifth Floor 24 * Boston, MA 02111-1301 USA 25 * 26 */ 27 28#include <linux/in.h> 29#include <linux/module.h> 30#include <linux/net.h> 31#include <linux/ipv6.h> 32#include <linux/errno.h> 33#include <linux/kernel.h> 34#include <linux/un.h> 35#include <asm/uaccess.h> 36#include <linux/inet.h> 37#include <linux/idr.h> 38#include <linux/file.h> 39 40#include "debug.h" 41#include "v9fs.h" 42#include "transport.h" 43 44#define V9FS_PORT 564 45 46struct v9fs_trans_fd { 47 struct file *rd; 48 struct file *wr; 49}; 50 51/** 52 * v9fs_fd_read- read from a fd 53 * @v9ses: session information 54 * @v: buffer to receive data into 55 * @len: size of receive buffer 56 * 57 */ 58static int v9fs_fd_read(struct v9fs_transport *trans, void *v, int len) 59{ 60 int ret; 61 struct v9fs_trans_fd *ts; 62 63 if (!trans || trans->status == Disconnected || !(ts = trans->priv)) 64 return -EREMOTEIO; 65 66 if (!(ts->rd->f_flags & O_NONBLOCK)) 67 dprintk(DEBUG_ERROR, "blocking read ...\n"); 68 69 ret = kernel_read(ts->rd, ts->rd->f_pos, v, len); 70 if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN) 71 trans->status = Disconnected; 72 return ret; 73} 74 75/** 76 * v9fs_fd_write - write to a socket 77 * @v9ses: session information 78 * @v: buffer to send data from 79 * @len: size of send buffer 80 * 81 */ 82static int v9fs_fd_write(struct v9fs_transport *trans, void *v, int len) 83{ 84 int ret; 85 mm_segment_t oldfs; 86 struct v9fs_trans_fd *ts; 87 88 if (!trans || trans->status == Disconnected || !(ts = trans->priv)) 89 return -EREMOTEIO; 90 91 if (!(ts->wr->f_flags & O_NONBLOCK)) 92 dprintk(DEBUG_ERROR, "blocking write ...\n"); 93 94 oldfs = get_fs(); 95 set_fs(get_ds()); 96 /* The cast to a user pointer is valid due to the set_fs() */ 97 ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos); 98 set_fs(oldfs); 99 100 if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN) 101 trans->status = Disconnected; 102 return ret; 103} 104 105static unsigned int 106v9fs_fd_poll(struct v9fs_transport *trans, struct poll_table_struct *pt) 107{ 108 int ret, n; 109 struct v9fs_trans_fd *ts; 110 mm_segment_t oldfs; 111 112 if (!trans || trans->status != Connected || !(ts = trans->priv)) 113 return -EREMOTEIO; 114 115 if (!ts->rd->f_op || !ts->rd->f_op->poll) 116 return -EIO; 117 118 if (!ts->wr->f_op || !ts->wr->f_op->poll) 119 return -EIO; 120 121 oldfs = get_fs(); 122 set_fs(get_ds()); 123 124 ret = ts->rd->f_op->poll(ts->rd, pt); 125 if (ret < 0) 126 goto end; 127 128 if (ts->rd != ts->wr) { 129 n = ts->wr->f_op->poll(ts->wr, pt); 130 if (n < 0) { 131 ret = n; 132 goto end; 133 } 134 ret = (ret & ~POLLOUT) | (n & ~POLLIN); 135 } 136 137 end: 138 set_fs(oldfs); 139 return ret; 140} 141 142static int v9fs_fd_open(struct v9fs_session_info *v9ses, int rfd, int wfd) 143{ 144 struct v9fs_transport *trans = v9ses->transport; 145 struct v9fs_trans_fd *ts = kmalloc(sizeof(struct v9fs_trans_fd), 146 GFP_KERNEL); 147 if (!ts) 148 return -ENOMEM; 149 150 ts->rd = fget(rfd); 151 ts->wr = fget(wfd); 152 if (!ts->rd || !ts->wr) { 153 if (ts->rd) 154 fput(ts->rd); 155 if (ts->wr) 156 fput(ts->wr); 157 kfree(ts); 158 return -EIO; 159 } 160 161 trans->priv = ts; 162 trans->status = Connected; 163 164 return 0; 165} 166 167static int v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, 168 char *data) 169{ 170 if (v9ses->rfdno == ~0 || v9ses->wfdno == ~0) { 171 printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); 172 return -ENOPROTOOPT; 173 } 174 175 return v9fs_fd_open(v9ses, v9ses->rfdno, v9ses->wfdno); 176} 177 178static int v9fs_socket_open(struct v9fs_session_info *v9ses, 179 struct socket *csocket) 180{ 181 int fd, ret; 182 183 csocket->sk->sk_allocation = GFP_NOIO; 184 if ((fd = sock_map_fd(csocket)) < 0) { 185 eprintk(KERN_ERR, "v9fs_socket_open: failed to map fd\n"); 186 ret = fd; 187 release_csocket: 188 sock_release(csocket); 189 return ret; 190 } 191 192 if ((ret = v9fs_fd_open(v9ses, fd, fd)) < 0) { 193 sockfd_put(csocket); 194 eprintk(KERN_ERR, "v9fs_socket_open: failed to open fd\n"); 195 goto release_csocket; 196 } 197 198 ((struct v9fs_trans_fd *)v9ses->transport->priv)->rd->f_flags |= 199 O_NONBLOCK; 200 return 0; 201} 202 203static int v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, 204 char *data) 205{ 206 int ret; 207 struct socket *csocket = NULL; 208 struct sockaddr_in sin_server; 209 210 sin_server.sin_family = AF_INET; 211 sin_server.sin_addr.s_addr = in_aton(addr); 212 sin_server.sin_port = htons(v9ses->port); 213 sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket); 214 215 if (!csocket) { 216 eprintk(KERN_ERR, "v9fs_trans_tcp: problem creating socket\n"); 217 return -1; 218 } 219 220 ret = csocket->ops->connect(csocket, 221 (struct sockaddr *)&sin_server, 222 sizeof(struct sockaddr_in), 0); 223 if (ret < 0) { 224 eprintk(KERN_ERR, 225 "v9fs_trans_tcp: problem connecting socket to %s\n", 226 addr); 227 return ret; 228 } 229 230 return v9fs_socket_open(v9ses, csocket); 231} 232 233static int 234v9fs_unix_init(struct v9fs_session_info *v9ses, const char *addr, char *data) 235{ 236 int ret; 237 struct socket *csocket; 238 struct sockaddr_un sun_server; 239 240 if (strlen(addr) > UNIX_PATH_MAX) { 241 eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n", 242 addr); 243 return -ENAMETOOLONG; 244 } 245 246 sun_server.sun_family = PF_UNIX; 247 strcpy(sun_server.sun_path, addr); 248 sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket); 249 ret = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server, 250 sizeof(struct sockaddr_un) - 1, 0); 251 if (ret < 0) { 252 eprintk(KERN_ERR, 253 "v9fs_trans_unix: problem connecting socket: %s: %d\n", 254 addr, ret); 255 return ret; 256 } 257 258 return v9fs_socket_open(v9ses, csocket); 259} 260 261/** 262 * v9fs_sock_close - shutdown socket 263 * @trans: private socket structure 264 * 265 */ 266static void v9fs_fd_close(struct v9fs_transport *trans) 267{ 268 struct v9fs_trans_fd *ts; 269 270 if (!trans) 271 return; 272 273 ts = xchg(&trans->priv, NULL); 274 275 if (!ts) 276 return; 277 278 trans->status = Disconnected; 279 if (ts->rd) 280 fput(ts->rd); 281 if (ts->wr) 282 fput(ts->wr); 283 kfree(ts); 284} 285 286struct v9fs_transport v9fs_trans_fd = { 287 .init = v9fs_fd_init, 288 .write = v9fs_fd_write, 289 .read = v9fs_fd_read, 290 .close = v9fs_fd_close, 291 .poll = v9fs_fd_poll, 292}; 293 294struct v9fs_transport v9fs_trans_tcp = { 295 .init = v9fs_tcp_init, 296 .write = v9fs_fd_write, 297 .read = v9fs_fd_read, 298 .close = v9fs_fd_close, 299 .poll = v9fs_fd_poll, 300}; 301 302struct v9fs_transport v9fs_trans_unix = { 303 .init = v9fs_unix_init, 304 .write = v9fs_fd_write, 305 .read = v9fs_fd_read, 306 .close = v9fs_fd_close, 307 .poll = v9fs_fd_poll, 308}; 309