xref: /openbmc/linux/net/9p/trans_fd.c (revision 643d1f7f)
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-2007 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 <linux/uaccess.h>
36 #include <linux/inet.h>
37 #include <linux/idr.h>
38 #include <linux/file.h>
39 #include <linux/parser.h>
40 #include <net/9p/9p.h>
41 #include <net/9p/transport.h>
42 
43 #define P9_PORT 564
44 #define MAX_SOCK_BUF (64*1024)
45 
46 
47 struct p9_fd_opts {
48 	int rfd;
49 	int wfd;
50 	u16 port;
51 };
52 
53 struct p9_trans_fd {
54 	struct file *rd;
55 	struct file *wr;
56 };
57 
58 /*
59   * Option Parsing (code inspired by NFS code)
60   *  - a little lazy - parse all fd-transport options
61   */
62 
63 enum {
64 	/* Options that take integer arguments */
65 	Opt_port, Opt_rfdno, Opt_wfdno, Opt_err,
66 };
67 
68 static match_table_t tokens = {
69 	{Opt_port, "port=%u"},
70 	{Opt_rfdno, "rfdno=%u"},
71 	{Opt_wfdno, "wfdno=%u"},
72 	{Opt_err, NULL},
73 };
74 
75 /**
76  * v9fs_parse_options - parse mount options into session structure
77  * @options: options string passed from mount
78  * @v9ses: existing v9fs session information
79  *
80  */
81 
82 static void parse_opts(char *options, struct p9_fd_opts *opts)
83 {
84 	char *p;
85 	substring_t args[MAX_OPT_ARGS];
86 	int option;
87 	int ret;
88 
89 	opts->port = P9_PORT;
90 	opts->rfd = ~0;
91 	opts->wfd = ~0;
92 
93 	if (!options)
94 		return;
95 
96 	while ((p = strsep(&options, ",")) != NULL) {
97 		int token;
98 		if (!*p)
99 			continue;
100 		token = match_token(p, tokens, args);
101 		ret = match_int(&args[0], &option);
102 		if (ret < 0) {
103 			P9_DPRINTK(P9_DEBUG_ERROR,
104 			 "integer field, but no integer?\n");
105 			continue;
106 		}
107 		switch (token) {
108 		case Opt_port:
109 			opts->port = option;
110 			break;
111 		case Opt_rfdno:
112 			opts->rfd = option;
113 			break;
114 		case Opt_wfdno:
115 			opts->wfd = option;
116 			break;
117 		default:
118 			continue;
119 		}
120 	}
121 }
122 
123 static int p9_fd_open(struct p9_trans *trans, int rfd, int wfd)
124 {
125 	struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd),
126 					   GFP_KERNEL);
127 	if (!ts)
128 		return -ENOMEM;
129 
130 	ts->rd = fget(rfd);
131 	ts->wr = fget(wfd);
132 	if (!ts->rd || !ts->wr) {
133 		if (ts->rd)
134 			fput(ts->rd);
135 		if (ts->wr)
136 			fput(ts->wr);
137 		kfree(ts);
138 		return -EIO;
139 	}
140 
141 	trans->priv = ts;
142 	trans->status = Connected;
143 
144 	return 0;
145 }
146 
147 static int p9_socket_open(struct p9_trans *trans, struct socket *csocket)
148 {
149 	int fd, ret;
150 
151 	csocket->sk->sk_allocation = GFP_NOIO;
152 	fd = sock_map_fd(csocket);
153 	if (fd < 0) {
154 		P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to map fd\n");
155 		return fd;
156 	}
157 
158 	ret = p9_fd_open(trans, fd, fd);
159 	if (ret < 0) {
160 		P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
161 		sockfd_put(csocket);
162 		return ret;
163 	}
164 
165 	((struct p9_trans_fd *)trans->priv)->rd->f_flags |= O_NONBLOCK;
166 
167 	return 0;
168 }
169 
170 /**
171  * p9_fd_read- read from a fd
172  * @v9ses: session information
173  * @v: buffer to receive data into
174  * @len: size of receive buffer
175  *
176  */
177 static int p9_fd_read(struct p9_trans *trans, void *v, int len)
178 {
179 	int ret;
180 	struct p9_trans_fd *ts = NULL;
181 
182 	if (trans && trans->status != Disconnected)
183 		ts = trans->priv;
184 
185 	if (!ts)
186 		return -EREMOTEIO;
187 
188 	if (!(ts->rd->f_flags & O_NONBLOCK))
189 		P9_DPRINTK(P9_DEBUG_ERROR, "blocking read ...\n");
190 
191 	ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
192 	if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
193 		trans->status = Disconnected;
194 	return ret;
195 }
196 
197 /**
198  * p9_fd_write - write to a socket
199  * @v9ses: session information
200  * @v: buffer to send data from
201  * @len: size of send buffer
202  *
203  */
204 static int p9_fd_write(struct p9_trans *trans, void *v, int len)
205 {
206 	int ret;
207 	mm_segment_t oldfs;
208 	struct p9_trans_fd *ts = NULL;
209 
210 	if (trans && trans->status != Disconnected)
211 		ts = trans->priv;
212 
213 	if (!ts)
214 		return -EREMOTEIO;
215 
216 	if (!(ts->wr->f_flags & O_NONBLOCK))
217 		P9_DPRINTK(P9_DEBUG_ERROR, "blocking write ...\n");
218 
219 	oldfs = get_fs();
220 	set_fs(get_ds());
221 	/* The cast to a user pointer is valid due to the set_fs() */
222 	ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
223 	set_fs(oldfs);
224 
225 	if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
226 		trans->status = Disconnected;
227 	return ret;
228 }
229 
230 static unsigned int
231 p9_fd_poll(struct p9_trans *trans, struct poll_table_struct *pt)
232 {
233 	int ret, n;
234 	struct p9_trans_fd *ts = NULL;
235 	mm_segment_t oldfs;
236 
237 	if (trans && trans->status == Connected)
238 		ts = trans->priv;
239 
240 	if (!ts)
241 		return -EREMOTEIO;
242 
243 	if (!ts->rd->f_op || !ts->rd->f_op->poll)
244 		return -EIO;
245 
246 	if (!ts->wr->f_op || !ts->wr->f_op->poll)
247 		return -EIO;
248 
249 	oldfs = get_fs();
250 	set_fs(get_ds());
251 
252 	ret = ts->rd->f_op->poll(ts->rd, pt);
253 	if (ret < 0)
254 		goto end;
255 
256 	if (ts->rd != ts->wr) {
257 		n = ts->wr->f_op->poll(ts->wr, pt);
258 		if (n < 0) {
259 			ret = n;
260 			goto end;
261 		}
262 		ret = (ret & ~POLLOUT) | (n & ~POLLIN);
263 	}
264 
265 end:
266 	set_fs(oldfs);
267 	return ret;
268 }
269 
270 /**
271  * p9_sock_close - shutdown socket
272  * @trans: private socket structure
273  *
274  */
275 static void p9_fd_close(struct p9_trans *trans)
276 {
277 	struct p9_trans_fd *ts;
278 
279 	if (!trans)
280 		return;
281 
282 	ts = xchg(&trans->priv, NULL);
283 
284 	if (!ts)
285 		return;
286 
287 	trans->status = Disconnected;
288 	if (ts->rd)
289 		fput(ts->rd);
290 	if (ts->wr)
291 		fput(ts->wr);
292 	kfree(ts);
293 }
294 
295 static struct p9_trans *p9_trans_create_tcp(const char *addr, char *args)
296 {
297 	int err;
298 	struct p9_trans *trans;
299 	struct socket *csocket;
300 	struct sockaddr_in sin_server;
301 	struct p9_fd_opts opts;
302 
303 	parse_opts(args, &opts);
304 
305 	csocket = NULL;
306 	trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
307 	if (!trans)
308 		return ERR_PTR(-ENOMEM);
309 
310 	trans->write = p9_fd_write;
311 	trans->read = p9_fd_read;
312 	trans->close = p9_fd_close;
313 	trans->poll = p9_fd_poll;
314 
315 	sin_server.sin_family = AF_INET;
316 	sin_server.sin_addr.s_addr = in_aton(addr);
317 	sin_server.sin_port = htons(opts.port);
318 	sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
319 
320 	if (!csocket) {
321 		P9_EPRINTK(KERN_ERR, "p9_trans_tcp: problem creating socket\n");
322 		err = -EIO;
323 		goto error;
324 	}
325 
326 	err = csocket->ops->connect(csocket,
327 				    (struct sockaddr *)&sin_server,
328 				    sizeof(struct sockaddr_in), 0);
329 	if (err < 0) {
330 		P9_EPRINTK(KERN_ERR,
331 			"p9_trans_tcp: problem connecting socket to %s\n",
332 			addr);
333 		goto error;
334 	}
335 
336 	err = p9_socket_open(trans, csocket);
337 	if (err < 0)
338 		goto error;
339 
340 	return trans;
341 
342 error:
343 	if (csocket)
344 		sock_release(csocket);
345 
346 	kfree(trans);
347 	return ERR_PTR(err);
348 }
349 
350 static struct p9_trans *p9_trans_create_unix(const char *addr, char *args)
351 {
352 	int err;
353 	struct socket *csocket;
354 	struct sockaddr_un sun_server;
355 	struct p9_trans *trans;
356 
357 	csocket = NULL;
358 	trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
359 	if (!trans)
360 		return ERR_PTR(-ENOMEM);
361 
362 	trans->write = p9_fd_write;
363 	trans->read = p9_fd_read;
364 	trans->close = p9_fd_close;
365 	trans->poll = p9_fd_poll;
366 
367 	if (strlen(addr) > UNIX_PATH_MAX) {
368 		P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
369 			addr);
370 		err = -ENAMETOOLONG;
371 		goto error;
372 	}
373 
374 	sun_server.sun_family = PF_UNIX;
375 	strcpy(sun_server.sun_path, addr);
376 	sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
377 	err = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
378 			sizeof(struct sockaddr_un) - 1, 0);
379 	if (err < 0) {
380 		P9_EPRINTK(KERN_ERR,
381 			"p9_trans_unix: problem connecting socket: %s: %d\n",
382 			addr, err);
383 		goto error;
384 	}
385 
386 	err = p9_socket_open(trans, csocket);
387 	if (err < 0)
388 		goto error;
389 
390 	return trans;
391 
392 error:
393 	if (csocket)
394 		sock_release(csocket);
395 
396 	kfree(trans);
397 	return ERR_PTR(err);
398 }
399 
400 static struct p9_trans *p9_trans_create_fd(const char *name, char *args)
401 {
402 	int err;
403 	struct p9_trans *trans;
404 	struct p9_fd_opts opts;
405 
406 	parse_opts(args, &opts);
407 
408 	if (opts.rfd == ~0 || opts.wfd == ~0) {
409 		printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
410 		return ERR_PTR(-ENOPROTOOPT);
411 	}
412 
413 	trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
414 	if (!trans)
415 		return ERR_PTR(-ENOMEM);
416 
417 	trans->write = p9_fd_write;
418 	trans->read = p9_fd_read;
419 	trans->close = p9_fd_close;
420 	trans->poll = p9_fd_poll;
421 
422 	err = p9_fd_open(trans, opts.rfd, opts.wfd);
423 	if (err < 0)
424 		goto error;
425 
426 	return trans;
427 
428 error:
429 	kfree(trans);
430 	return ERR_PTR(err);
431 }
432 
433 static struct p9_trans_module p9_tcp_trans = {
434 	.name = "tcp",
435 	.maxsize = MAX_SOCK_BUF,
436 	.def = 1,
437 	.create = p9_trans_create_tcp,
438 };
439 
440 static struct p9_trans_module p9_unix_trans = {
441 	.name = "unix",
442 	.maxsize = MAX_SOCK_BUF,
443 	.def = 0,
444 	.create = p9_trans_create_unix,
445 };
446 
447 static struct p9_trans_module p9_fd_trans = {
448 	.name = "fd",
449 	.maxsize = MAX_SOCK_BUF,
450 	.def = 0,
451 	.create = p9_trans_create_fd,
452 };
453 
454 static int __init p9_trans_fd_init(void)
455 {
456 	v9fs_register_trans(&p9_tcp_trans);
457 	v9fs_register_trans(&p9_unix_trans);
458 	v9fs_register_trans(&p9_fd_trans);
459 
460 	return 1;
461 }
462 
463 static void __exit p9_trans_fd_exit(void) {
464 	printk(KERN_ERR "Removal of 9p transports not implemented\n");
465 	BUG();
466 }
467 
468 module_init(p9_trans_fd_init);
469 module_exit(p9_trans_fd_exit);
470 
471 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
472 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
473 MODULE_LICENSE("GPL");
474