2 * linux/fs/9p/trans_fd.c
4 * Fd transport layer. Includes deprecated socket layer.
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>
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.
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.
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
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>
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>
44 #define MAX_SOCK_BUF (64*1024)
59 * Option Parsing (code inspired by NFS code)
60 * - a little lazy - parse all fd-transport options
64 /* Options that take integer arguments */
65 Opt_port
, Opt_rfdno
, Opt_wfdno
,
68 static match_table_t tokens
= {
69 {Opt_port
, "port=%u"},
70 {Opt_rfdno
, "rfdno=%u"},
71 {Opt_wfdno
, "wfdno=%u"},
75 * v9fs_parse_options - parse mount options into session structure
76 * @options: options string passed from mount
77 * @v9ses: existing v9fs session information
81 static void parse_opts(char *options
, struct p9_fd_opts
*opts
)
84 substring_t args
[MAX_OPT_ARGS
];
95 while ((p
= strsep(&options
, ",")) != NULL
) {
99 token
= match_token(p
, tokens
, args
);
100 ret
= match_int(&args
[0], &option
);
102 P9_DPRINTK(P9_DEBUG_ERROR
,
103 "integer field, but no integer?\n");
122 static int p9_fd_open(struct p9_trans
*trans
, int rfd
, int wfd
)
124 struct p9_trans_fd
*ts
= kmalloc(sizeof(struct p9_trans_fd
),
131 if (!ts
->rd
|| !ts
->wr
) {
141 trans
->status
= Connected
;
146 static int p9_socket_open(struct p9_trans
*trans
, struct socket
*csocket
)
150 csocket
->sk
->sk_allocation
= GFP_NOIO
;
151 fd
= sock_map_fd(csocket
);
153 P9_EPRINTK(KERN_ERR
, "p9_socket_open: failed to map fd\n");
157 ret
= p9_fd_open(trans
, fd
, fd
);
159 P9_EPRINTK(KERN_ERR
, "p9_socket_open: failed to open fd\n");
164 ((struct p9_trans_fd
*)trans
->priv
)->rd
->f_flags
|= O_NONBLOCK
;
170 * p9_fd_read- read from a fd
171 * @v9ses: session information
172 * @v: buffer to receive data into
173 * @len: size of receive buffer
176 static int p9_fd_read(struct p9_trans
*trans
, void *v
, int len
)
179 struct p9_trans_fd
*ts
= NULL
;
181 if (trans
&& trans
->status
!= Disconnected
)
187 if (!(ts
->rd
->f_flags
& O_NONBLOCK
))
188 P9_DPRINTK(P9_DEBUG_ERROR
, "blocking read ...\n");
190 ret
= kernel_read(ts
->rd
, ts
->rd
->f_pos
, v
, len
);
191 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
192 trans
->status
= Disconnected
;
197 * p9_fd_write - write to a socket
198 * @v9ses: session information
199 * @v: buffer to send data from
200 * @len: size of send buffer
203 static int p9_fd_write(struct p9_trans
*trans
, void *v
, int len
)
207 struct p9_trans_fd
*ts
= NULL
;
209 if (trans
&& trans
->status
!= Disconnected
)
215 if (!(ts
->wr
->f_flags
& O_NONBLOCK
))
216 P9_DPRINTK(P9_DEBUG_ERROR
, "blocking write ...\n");
220 /* The cast to a user pointer is valid due to the set_fs() */
221 ret
= vfs_write(ts
->wr
, (void __user
*)v
, len
, &ts
->wr
->f_pos
);
224 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
225 trans
->status
= Disconnected
;
230 p9_fd_poll(struct p9_trans
*trans
, struct poll_table_struct
*pt
)
233 struct p9_trans_fd
*ts
= NULL
;
236 if (trans
&& trans
->status
== Connected
)
242 if (!ts
->rd
->f_op
|| !ts
->rd
->f_op
->poll
)
245 if (!ts
->wr
->f_op
|| !ts
->wr
->f_op
->poll
)
251 ret
= ts
->rd
->f_op
->poll(ts
->rd
, pt
);
255 if (ts
->rd
!= ts
->wr
) {
256 n
= ts
->wr
->f_op
->poll(ts
->wr
, pt
);
261 ret
= (ret
& ~POLLOUT
) | (n
& ~POLLIN
);
270 * p9_sock_close - shutdown socket
271 * @trans: private socket structure
274 static void p9_fd_close(struct p9_trans
*trans
)
276 struct p9_trans_fd
*ts
;
281 ts
= xchg(&trans
->priv
, NULL
);
286 trans
->status
= Disconnected
;
294 static struct p9_trans
*p9_trans_create_tcp(const char *addr
, char *args
)
297 struct p9_trans
*trans
;
298 struct socket
*csocket
;
299 struct sockaddr_in sin_server
;
300 struct p9_fd_opts opts
;
302 parse_opts(args
, &opts
);
305 trans
= kmalloc(sizeof(struct p9_trans
), GFP_KERNEL
);
307 return ERR_PTR(-ENOMEM
);
309 trans
->write
= p9_fd_write
;
310 trans
->read
= p9_fd_read
;
311 trans
->close
= p9_fd_close
;
312 trans
->poll
= p9_fd_poll
;
314 sin_server
.sin_family
= AF_INET
;
315 sin_server
.sin_addr
.s_addr
= in_aton(addr
);
316 sin_server
.sin_port
= htons(opts
.port
);
317 sock_create_kern(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
, &csocket
);
320 P9_EPRINTK(KERN_ERR
, "p9_trans_tcp: problem creating socket\n");
325 err
= csocket
->ops
->connect(csocket
,
326 (struct sockaddr
*)&sin_server
,
327 sizeof(struct sockaddr_in
), 0);
330 "p9_trans_tcp: problem connecting socket to %s\n",
335 err
= p9_socket_open(trans
, csocket
);
343 sock_release(csocket
);
349 static struct p9_trans
*p9_trans_create_unix(const char *addr
, char *args
)
352 struct socket
*csocket
;
353 struct sockaddr_un sun_server
;
354 struct p9_trans
*trans
;
357 trans
= kmalloc(sizeof(struct p9_trans
), GFP_KERNEL
);
359 return ERR_PTR(-ENOMEM
);
361 trans
->write
= p9_fd_write
;
362 trans
->read
= p9_fd_read
;
363 trans
->close
= p9_fd_close
;
364 trans
->poll
= p9_fd_poll
;
366 if (strlen(addr
) > UNIX_PATH_MAX
) {
367 P9_EPRINTK(KERN_ERR
, "p9_trans_unix: address too long: %s\n",
373 sun_server
.sun_family
= PF_UNIX
;
374 strcpy(sun_server
.sun_path
, addr
);
375 sock_create_kern(PF_UNIX
, SOCK_STREAM
, 0, &csocket
);
376 err
= csocket
->ops
->connect(csocket
, (struct sockaddr
*)&sun_server
,
377 sizeof(struct sockaddr_un
) - 1, 0);
380 "p9_trans_unix: problem connecting socket: %s: %d\n",
385 err
= p9_socket_open(trans
, csocket
);
393 sock_release(csocket
);
399 static struct p9_trans
*p9_trans_create_fd(const char *name
, char *args
)
402 struct p9_trans
*trans
;
403 struct p9_fd_opts opts
;
405 parse_opts(args
, &opts
);
407 if (opts
.rfd
== ~0 || opts
.wfd
== ~0) {
408 printk(KERN_ERR
"v9fs: Insufficient options for proto=fd\n");
409 return ERR_PTR(-ENOPROTOOPT
);
412 trans
= kmalloc(sizeof(struct p9_trans
), GFP_KERNEL
);
414 return ERR_PTR(-ENOMEM
);
416 trans
->write
= p9_fd_write
;
417 trans
->read
= p9_fd_read
;
418 trans
->close
= p9_fd_close
;
419 trans
->poll
= p9_fd_poll
;
421 err
= p9_fd_open(trans
, opts
.rfd
, opts
.wfd
);
432 static struct p9_trans_module p9_tcp_trans
= {
434 .maxsize
= MAX_SOCK_BUF
,
436 .create
= p9_trans_create_tcp
,
439 static struct p9_trans_module p9_unix_trans
= {
441 .maxsize
= MAX_SOCK_BUF
,
443 .create
= p9_trans_create_unix
,
446 static struct p9_trans_module p9_fd_trans
= {
448 .maxsize
= MAX_SOCK_BUF
,
450 .create
= p9_trans_create_fd
,
453 static int __init
p9_trans_fd_init(void)
455 v9fs_register_trans(&p9_tcp_trans
);
456 v9fs_register_trans(&p9_unix_trans
);
457 v9fs_register_trans(&p9_fd_trans
);
462 static void __exit
p9_trans_fd_exit(void) {
463 printk(KERN_ERR
"Removal of 9p transports not implemented\n");
467 module_init(p9_trans_fd_init
);
468 module_exit(p9_trans_fd_exit
);
470 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
471 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
472 MODULE_LICENSE("GPL");