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
28 #include <linux/config.h>
30 #include <linux/module.h>
31 #include <linux/net.h>
32 #include <linux/ipv6.h>
33 #include <linux/errno.h>
34 #include <linux/kernel.h>
36 #include <asm/uaccess.h>
37 #include <linux/inet.h>
38 #include <linux/idr.h>
39 #include <linux/file.h>
43 #include "transport.h"
47 struct v9fs_trans_fd
{
53 * v9fs_fd_read- read from a fd
54 * @v9ses: session information
55 * @v: buffer to receive data into
56 * @len: size of receive buffer
59 static int v9fs_fd_read(struct v9fs_transport
*trans
, void *v
, int len
)
62 struct v9fs_trans_fd
*ts
;
64 if (!trans
|| trans
->status
== Disconnected
|| !(ts
= trans
->priv
))
67 if (!(ts
->rd
->f_flags
& O_NONBLOCK
))
68 dprintk(DEBUG_ERROR
, "blocking read ...\n");
70 ret
= kernel_read(ts
->rd
, ts
->rd
->f_pos
, v
, len
);
71 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
72 trans
->status
= Disconnected
;
77 * v9fs_fd_write - write to a socket
78 * @v9ses: session information
79 * @v: buffer to send data from
80 * @len: size of send buffer
83 static int v9fs_fd_write(struct v9fs_transport
*trans
, void *v
, int len
)
87 struct v9fs_trans_fd
*ts
;
89 if (!trans
|| trans
->status
== Disconnected
|| !(ts
= trans
->priv
))
92 if (!(ts
->wr
->f_flags
& O_NONBLOCK
))
93 dprintk(DEBUG_ERROR
, "blocking write ...\n");
97 /* The cast to a user pointer is valid due to the set_fs() */
98 ret
= vfs_write(ts
->wr
, (void __user
*)v
, len
, &ts
->wr
->f_pos
);
101 if (ret
<= 0 && ret
!= -ERESTARTSYS
&& ret
!= -EAGAIN
)
102 trans
->status
= Disconnected
;
107 v9fs_fd_poll(struct v9fs_transport
*trans
, struct poll_table_struct
*pt
)
110 struct v9fs_trans_fd
*ts
;
113 if (!trans
|| trans
->status
!= Connected
|| !(ts
= trans
->priv
))
116 if (!ts
->rd
->f_op
|| !ts
->rd
->f_op
->poll
)
119 if (!ts
->wr
->f_op
|| !ts
->wr
->f_op
->poll
)
125 ret
= ts
->rd
->f_op
->poll(ts
->rd
, pt
);
129 if (ts
->rd
!= ts
->wr
) {
130 n
= ts
->wr
->f_op
->poll(ts
->wr
, pt
);
135 ret
= (ret
& ~POLLOUT
) | (n
& ~POLLIN
);
143 static int v9fs_fd_open(struct v9fs_session_info
*v9ses
, int rfd
, int wfd
)
145 struct v9fs_transport
*trans
= v9ses
->transport
;
146 struct v9fs_trans_fd
*ts
= kmalloc(sizeof(struct v9fs_trans_fd
),
153 if (!ts
->rd
|| !ts
->wr
) {
163 trans
->status
= Connected
;
168 static int v9fs_fd_init(struct v9fs_session_info
*v9ses
, const char *addr
,
171 if (v9ses
->rfdno
== ~0 || v9ses
->wfdno
== ~0) {
172 printk(KERN_ERR
"v9fs: Insufficient options for proto=fd\n");
176 return v9fs_fd_open(v9ses
, v9ses
->rfdno
, v9ses
->wfdno
);
179 static int v9fs_socket_open(struct v9fs_session_info
*v9ses
,
180 struct socket
*csocket
)
184 csocket
->sk
->sk_allocation
= GFP_NOIO
;
185 if ((fd
= sock_map_fd(csocket
)) < 0) {
186 eprintk(KERN_ERR
, "v9fs_socket_open: failed to map fd\n");
189 sock_release(csocket
);
193 if ((ret
= v9fs_fd_open(v9ses
, fd
, fd
)) < 0) {
195 eprintk(KERN_ERR
, "v9fs_socket_open: failed to open fd\n");
196 goto release_csocket
;
199 ((struct v9fs_trans_fd
*)v9ses
->transport
->priv
)->rd
->f_flags
|=
204 static int v9fs_tcp_init(struct v9fs_session_info
*v9ses
, const char *addr
,
208 struct socket
*csocket
= NULL
;
209 struct sockaddr_in sin_server
;
211 sin_server
.sin_family
= AF_INET
;
212 sin_server
.sin_addr
.s_addr
= in_aton(addr
);
213 sin_server
.sin_port
= htons(v9ses
->port
);
214 sock_create_kern(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
, &csocket
);
217 eprintk(KERN_ERR
, "v9fs_trans_tcp: problem creating socket\n");
221 ret
= csocket
->ops
->connect(csocket
,
222 (struct sockaddr
*)&sin_server
,
223 sizeof(struct sockaddr_in
), 0);
226 "v9fs_trans_tcp: problem connecting socket to %s\n",
231 return v9fs_socket_open(v9ses
, csocket
);
235 v9fs_unix_init(struct v9fs_session_info
*v9ses
, const char *addr
, char *data
)
238 struct socket
*csocket
;
239 struct sockaddr_un sun_server
;
241 if (strlen(addr
) > UNIX_PATH_MAX
) {
242 eprintk(KERN_ERR
, "v9fs_trans_unix: address too long: %s\n",
244 return -ENAMETOOLONG
;
247 sun_server
.sun_family
= PF_UNIX
;
248 strcpy(sun_server
.sun_path
, addr
);
249 sock_create_kern(PF_UNIX
, SOCK_STREAM
, 0, &csocket
);
250 ret
= csocket
->ops
->connect(csocket
, (struct sockaddr
*)&sun_server
,
251 sizeof(struct sockaddr_un
) - 1, 0);
254 "v9fs_trans_unix: problem connecting socket: %s: %d\n",
259 return v9fs_socket_open(v9ses
, csocket
);
263 * v9fs_sock_close - shutdown socket
264 * @trans: private socket structure
267 static void v9fs_fd_close(struct v9fs_transport
*trans
)
269 struct v9fs_trans_fd
*ts
;
274 ts
= xchg(&trans
->priv
, NULL
);
279 trans
->status
= Disconnected
;
287 struct v9fs_transport v9fs_trans_fd
= {
288 .init
= v9fs_fd_init
,
289 .write
= v9fs_fd_write
,
290 .read
= v9fs_fd_read
,
291 .close
= v9fs_fd_close
,
292 .poll
= v9fs_fd_poll
,
295 struct v9fs_transport v9fs_trans_tcp
= {
296 .init
= v9fs_tcp_init
,
297 .write
= v9fs_fd_write
,
298 .read
= v9fs_fd_read
,
299 .close
= v9fs_fd_close
,
300 .poll
= v9fs_fd_poll
,
303 struct v9fs_transport v9fs_trans_unix
= {
304 .init
= v9fs_unix_init
,
305 .write
= v9fs_fd_write
,
306 .read
= v9fs_fd_read
,
307 .close
= v9fs_fd_close
,
308 .poll
= v9fs_fd_poll
,