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
, Opt_err
,
68 static match_table_t tokens
= {
69 {Opt_port
, "port=%u"},
70 {Opt_rfdno
, "rfdno=%u"},
71 {Opt_wfdno
, "wfdno=%u"},
76 * v9fs_parse_options - parse mount options into session structure
77 * @options: options string passed from mount
78 * @v9ses: existing v9fs session information
82 static void parse_opts(char *options
, struct p9_fd_opts
*opts
)
85 substring_t args
[MAX_OPT_ARGS
];
96 while ((p
= strsep(&options
, ",")) != NULL
) {
100 token
= match_token(p
, tokens
, args
);
101 ret
= match_int(&args
[0], &option
);
103 P9_DPRINTK(P9_DEBUG_ERROR
,
104 "integer field, but no integer?\n");
123 static int p9_fd_open(struct p9_trans
*trans
, int rfd
, int wfd
)
125 struct p9_trans_fd
*ts
= kmalloc(sizeof(struct p9_trans_fd
),
132 if (!ts
->rd
|| !ts
->wr
) {
142 trans
->status
= Connected
;
147 static int p9_socket_open(struct p9_trans
*trans
, struct socket
*csocket
)
151 csocket
->sk
->sk_allocation
= GFP_NOIO
;
152 fd
= sock_map_fd(csocket
);
154 P9_EPRINTK(KERN_ERR
, "p9_socket_open: failed to map fd\n");
158 ret
= p9_fd_open(trans
, fd
, fd
);
160 P9_EPRINTK(KERN_ERR
, "p9_socket_open: failed to open fd\n");
165 ((struct p9_trans_fd
*)trans
->priv
)->rd
->f_flags
|= O_NONBLOCK
;
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
177 static int p9_fd_read(struct p9_trans
*trans
, void *v
, int len
)
180 struct p9_trans_fd
*ts
= NULL
;
182 if (trans
&& trans
->status
!= Disconnected
)
188 if (!(ts
->rd
->f_flags
& O_NONBLOCK
))
189 P9_DPRINTK(P9_DEBUG_ERROR
, "blocking read ...\n");
191 ret
= kernel_read(ts
->rd
, ts
->rd
->f_pos
, v
, len
);
192 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
193 trans
->status
= Disconnected
;
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
204 static int p9_fd_write(struct p9_trans
*trans
, void *v
, int len
)
208 struct p9_trans_fd
*ts
= NULL
;
210 if (trans
&& trans
->status
!= Disconnected
)
216 if (!(ts
->wr
->f_flags
& O_NONBLOCK
))
217 P9_DPRINTK(P9_DEBUG_ERROR
, "blocking write ...\n");
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
);
225 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
226 trans
->status
= Disconnected
;
231 p9_fd_poll(struct p9_trans
*trans
, struct poll_table_struct
*pt
)
234 struct p9_trans_fd
*ts
= NULL
;
237 if (trans
&& trans
->status
== Connected
)
243 if (!ts
->rd
->f_op
|| !ts
->rd
->f_op
->poll
)
246 if (!ts
->wr
->f_op
|| !ts
->wr
->f_op
->poll
)
252 ret
= ts
->rd
->f_op
->poll(ts
->rd
, pt
);
256 if (ts
->rd
!= ts
->wr
) {
257 n
= ts
->wr
->f_op
->poll(ts
->wr
, pt
);
262 ret
= (ret
& ~POLLOUT
) | (n
& ~POLLIN
);
271 * p9_sock_close - shutdown socket
272 * @trans: private socket structure
275 static void p9_fd_close(struct p9_trans
*trans
)
277 struct p9_trans_fd
*ts
;
282 ts
= xchg(&trans
->priv
, NULL
);
287 trans
->status
= Disconnected
;
295 static struct p9_trans
*p9_trans_create_tcp(const char *addr
, char *args
)
298 struct p9_trans
*trans
;
299 struct socket
*csocket
;
300 struct sockaddr_in sin_server
;
301 struct p9_fd_opts opts
;
303 parse_opts(args
, &opts
);
306 trans
= kmalloc(sizeof(struct p9_trans
), GFP_KERNEL
);
308 return ERR_PTR(-ENOMEM
);
310 trans
->write
= p9_fd_write
;
311 trans
->read
= p9_fd_read
;
312 trans
->close
= p9_fd_close
;
313 trans
->poll
= p9_fd_poll
;
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
);
321 P9_EPRINTK(KERN_ERR
, "p9_trans_tcp: problem creating socket\n");
326 err
= csocket
->ops
->connect(csocket
,
327 (struct sockaddr
*)&sin_server
,
328 sizeof(struct sockaddr_in
), 0);
331 "p9_trans_tcp: problem connecting socket to %s\n",
336 err
= p9_socket_open(trans
, csocket
);
344 sock_release(csocket
);
350 static struct p9_trans
*p9_trans_create_unix(const char *addr
, char *args
)
353 struct socket
*csocket
;
354 struct sockaddr_un sun_server
;
355 struct p9_trans
*trans
;
358 trans
= kmalloc(sizeof(struct p9_trans
), GFP_KERNEL
);
360 return ERR_PTR(-ENOMEM
);
362 trans
->write
= p9_fd_write
;
363 trans
->read
= p9_fd_read
;
364 trans
->close
= p9_fd_close
;
365 trans
->poll
= p9_fd_poll
;
367 if (strlen(addr
) > UNIX_PATH_MAX
) {
368 P9_EPRINTK(KERN_ERR
, "p9_trans_unix: address too long: %s\n",
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);
381 "p9_trans_unix: problem connecting socket: %s: %d\n",
386 err
= p9_socket_open(trans
, csocket
);
394 sock_release(csocket
);
400 static struct p9_trans
*p9_trans_create_fd(const char *name
, char *args
)
403 struct p9_trans
*trans
;
404 struct p9_fd_opts opts
;
406 parse_opts(args
, &opts
);
408 if (opts
.rfd
== ~0 || opts
.wfd
== ~0) {
409 printk(KERN_ERR
"v9fs: Insufficient options for proto=fd\n");
410 return ERR_PTR(-ENOPROTOOPT
);
413 trans
= kmalloc(sizeof(struct p9_trans
), GFP_KERNEL
);
415 return ERR_PTR(-ENOMEM
);
417 trans
->write
= p9_fd_write
;
418 trans
->read
= p9_fd_read
;
419 trans
->close
= p9_fd_close
;
420 trans
->poll
= p9_fd_poll
;
422 err
= p9_fd_open(trans
, opts
.rfd
, opts
.wfd
);
433 static struct p9_trans_module p9_tcp_trans
= {
435 .maxsize
= MAX_SOCK_BUF
,
437 .create
= p9_trans_create_tcp
,
440 static struct p9_trans_module p9_unix_trans
= {
442 .maxsize
= MAX_SOCK_BUF
,
444 .create
= p9_trans_create_unix
,
447 static struct p9_trans_module p9_fd_trans
= {
449 .maxsize
= MAX_SOCK_BUF
,
451 .create
= p9_trans_create_fd
,
454 static int __init
p9_trans_fd_init(void)
456 v9fs_register_trans(&p9_tcp_trans
);
457 v9fs_register_trans(&p9_unix_trans
);
458 v9fs_register_trans(&p9_fd_trans
);
463 static void __exit
p9_trans_fd_exit(void) {
464 printk(KERN_ERR
"Removal of 9p transports not implemented\n");
468 module_init(p9_trans_fd_init
);
469 module_exit(p9_trans_fd_exit
);
471 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
472 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
473 MODULE_LICENSE("GPL");