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-2005 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 <asm/uaccess.h>
36 #include <linux/inet.h>
37 #include <linux/idr.h>
38 #include <linux/file.h>
42 #include "transport.h"
46 struct v9fs_trans_fd
{
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
58 static int v9fs_fd_read(struct v9fs_transport
*trans
, void *v
, int len
)
61 struct v9fs_trans_fd
*ts
;
63 if (!trans
|| trans
->status
== Disconnected
|| !(ts
= trans
->priv
))
66 if (!(ts
->rd
->f_flags
& O_NONBLOCK
))
67 dprintk(DEBUG_ERROR
, "blocking read ...\n");
69 ret
= kernel_read(ts
->rd
, ts
->rd
->f_pos
, v
, len
);
70 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
71 trans
->status
= Disconnected
;
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
82 static int v9fs_fd_write(struct v9fs_transport
*trans
, void *v
, int len
)
86 struct v9fs_trans_fd
*ts
;
88 if (!trans
|| trans
->status
== Disconnected
|| !(ts
= trans
->priv
))
91 if (!(ts
->wr
->f_flags
& O_NONBLOCK
))
92 dprintk(DEBUG_ERROR
, "blocking write ...\n");
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
);
100 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
101 trans
->status
= Disconnected
;
106 v9fs_fd_poll(struct v9fs_transport
*trans
, struct poll_table_struct
*pt
)
109 struct v9fs_trans_fd
*ts
;
112 if (!trans
|| trans
->status
!= Connected
|| !(ts
= trans
->priv
))
115 if (!ts
->rd
->f_op
|| !ts
->rd
->f_op
->poll
)
118 if (!ts
->wr
->f_op
|| !ts
->wr
->f_op
->poll
)
124 ret
= ts
->rd
->f_op
->poll(ts
->rd
, pt
);
128 if (ts
->rd
!= ts
->wr
) {
129 n
= ts
->wr
->f_op
->poll(ts
->wr
, pt
);
134 ret
= (ret
& ~POLLOUT
) | (n
& ~POLLIN
);
142 static int v9fs_fd_open(struct v9fs_session_info
*v9ses
, int rfd
, int wfd
)
144 struct v9fs_transport
*trans
= v9ses
->transport
;
145 struct v9fs_trans_fd
*ts
= kmalloc(sizeof(struct v9fs_trans_fd
),
152 if (!ts
->rd
|| !ts
->wr
) {
162 trans
->status
= Connected
;
167 static int v9fs_fd_init(struct v9fs_session_info
*v9ses
, const char *addr
,
170 if (v9ses
->rfdno
== ~0 || v9ses
->wfdno
== ~0) {
171 printk(KERN_ERR
"v9fs: Insufficient options for proto=fd\n");
175 return v9fs_fd_open(v9ses
, v9ses
->rfdno
, v9ses
->wfdno
);
178 static int v9fs_socket_open(struct v9fs_session_info
*v9ses
,
179 struct socket
*csocket
)
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");
188 sock_release(csocket
);
192 if ((ret
= v9fs_fd_open(v9ses
, fd
, fd
)) < 0) {
194 eprintk(KERN_ERR
, "v9fs_socket_open: failed to open fd\n");
195 goto release_csocket
;
198 ((struct v9fs_trans_fd
*)v9ses
->transport
->priv
)->rd
->f_flags
|=
203 static int v9fs_tcp_init(struct v9fs_session_info
*v9ses
, const char *addr
,
207 struct socket
*csocket
= NULL
;
208 struct sockaddr_in sin_server
;
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
);
216 eprintk(KERN_ERR
, "v9fs_trans_tcp: problem creating socket\n");
220 ret
= csocket
->ops
->connect(csocket
,
221 (struct sockaddr
*)&sin_server
,
222 sizeof(struct sockaddr_in
), 0);
225 "v9fs_trans_tcp: problem connecting socket to %s\n",
230 return v9fs_socket_open(v9ses
, csocket
);
234 v9fs_unix_init(struct v9fs_session_info
*v9ses
, const char *addr
, char *data
)
237 struct socket
*csocket
;
238 struct sockaddr_un sun_server
;
240 if (strlen(addr
) > UNIX_PATH_MAX
) {
241 eprintk(KERN_ERR
, "v9fs_trans_unix: address too long: %s\n",
243 return -ENAMETOOLONG
;
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);
253 "v9fs_trans_unix: problem connecting socket: %s: %d\n",
258 return v9fs_socket_open(v9ses
, csocket
);
262 * v9fs_sock_close - shutdown socket
263 * @trans: private socket structure
266 static void v9fs_fd_close(struct v9fs_transport
*trans
)
268 struct v9fs_trans_fd
*ts
;
273 ts
= xchg(&trans
->priv
, NULL
);
278 trans
->status
= Disconnected
;
286 struct 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
,
294 struct 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
,
302 struct 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
,