]> bbs.cooldavid.org Git - net-next-2.6.git/blob - fs/9p/trans_sock.c
[PATCH] v9fs: new multiplexer implementation
[net-next-2.6.git] / fs / 9p / trans_sock.c
1 /*
2  * linux/fs/9p/trans_socket.c
3  *
4  * Socket Transport Layer
5  *
6  *  Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
7  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
8  *  Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
9  *  Copyright (C) 1995, 1996 by Olaf Kirch <okir@monad.swb.de>
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 as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to:
23  *  Free Software Foundation
24  *  51 Franklin Street, Fifth Floor
25  *  Boston, MA  02111-1301  USA
26  *
27  */
28
29 #include <linux/config.h>
30 #include <linux/in.h>
31 #include <linux/module.h>
32 #include <linux/net.h>
33 #include <linux/ipv6.h>
34 #include <linux/errno.h>
35 #include <linux/kernel.h>
36 #include <linux/un.h>
37 #include <asm/uaccess.h>
38 #include <linux/inet.h>
39 #include <linux/idr.h>
40 #include <linux/file.h>
41
42 #include "debug.h"
43 #include "v9fs.h"
44 #include "transport.h"
45
46 #define V9FS_PORT 564
47
48 struct v9fs_trans_sock {
49         struct socket *s;
50         struct file *filp;
51 };
52
53 /**
54  * v9fs_sock_recv - receive from a socket
55  * @v9ses: session information
56  * @v: buffer to receive data into
57  * @len: size of receive buffer
58  *
59  */
60
61 static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len)
62 {
63         int ret;
64         struct v9fs_trans_sock *ts;
65
66         if (!trans || trans->status == Disconnected) {
67                 dprintk(DEBUG_ERROR, "disconnected ...\n");
68                 return -EREMOTEIO;
69         }
70
71         ts = trans->priv;
72
73         if (!(ts->filp->f_flags & O_NONBLOCK))
74                 dprintk(DEBUG_ERROR, "blocking read ...\n");
75
76         ret = kernel_read(ts->filp, ts->filp->f_pos, v, len);
77         if (ret <= 0) {
78                 if (ret != -ERESTARTSYS && ret != -EAGAIN)
79                         trans->status = Disconnected;
80         }
81
82         return ret;
83 }
84
85 /**
86  * v9fs_sock_send - send to a socket
87  * @v9ses: session information
88  * @v: buffer to send data from
89  * @len: size of send buffer
90  *
91  */
92
93 static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len)
94 {
95         int ret;
96         mm_segment_t oldfs;
97         struct v9fs_trans_sock *ts;
98
99         if (!trans || trans->status == Disconnected) {
100                 dprintk(DEBUG_ERROR, "disconnected ...\n");
101                 return -EREMOTEIO;
102         }
103
104         ts = trans->priv;
105         if (!ts) {
106                 dprintk(DEBUG_ERROR, "no transport ...\n");
107                 return -EREMOTEIO;
108         }
109
110         if (!(ts->filp->f_flags & O_NONBLOCK))
111                 dprintk(DEBUG_ERROR, "blocking write ...\n");
112
113         dump_data(v, len);
114         oldfs = get_fs();
115         set_fs(get_ds());
116         ret = vfs_write(ts->filp, (void __user *)v, len, &ts->filp->f_pos);
117         set_fs(oldfs);
118
119         if (ret < 0) {
120                 if (ret != -ERESTARTSYS)
121                         trans->status = Disconnected;
122         }
123
124         return ret;
125 }
126
127 static unsigned int v9fs_sock_poll(struct v9fs_transport *trans,
128         struct poll_table_struct *pt) {
129
130         int ret;
131         struct v9fs_trans_sock *ts;
132         mm_segment_t oldfs;
133
134         if (!trans) {
135                 dprintk(DEBUG_ERROR, "no transport\n");
136                 return -EIO;
137         }
138
139         ts = trans->priv;
140         if (trans->status != Connected || !ts) {
141                 dprintk(DEBUG_ERROR, "transport disconnected: %d\n", trans->status);
142                 return -EIO;
143         }
144
145         oldfs = get_fs();
146         set_fs(get_ds());
147
148         if (!ts->filp->f_op || !ts->filp->f_op->poll) {
149                 dprintk(DEBUG_ERROR, "no poll operation\n");
150                 ret = -EIO;
151                 goto end;
152         }
153
154         ret = ts->filp->f_op->poll(ts->filp, pt);
155
156 end:
157         set_fs(oldfs);
158         return ret;
159 }
160
161
162 /**
163  * v9fs_tcp_init - initialize TCP socket
164  * @v9ses: session information
165  * @addr: address of server to mount
166  * @data: mount options
167  *
168  */
169
170 static int
171 v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data)
172 {
173         struct socket *csocket = NULL;
174         struct sockaddr_in sin_server;
175         int rc = 0;
176         struct v9fs_trans_sock *ts = NULL;
177         struct v9fs_transport *trans = v9ses->transport;
178         int fd;
179
180         trans->status = Disconnected;
181
182         ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
183
184         if (!ts)
185                 return -ENOMEM;
186
187         trans->priv = ts;
188         ts->s = NULL;
189         ts->filp = NULL;
190
191         if (!addr)
192                 return -EINVAL;
193
194         dprintk(DEBUG_TRANS, "Connecting to %s\n", addr);
195
196         sin_server.sin_family = AF_INET;
197         sin_server.sin_addr.s_addr = in_aton(addr);
198         sin_server.sin_port = htons(v9ses->port);
199         sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
200         rc = csocket->ops->connect(csocket,
201                                    (struct sockaddr *)&sin_server,
202                                    sizeof(struct sockaddr_in), 0);
203         if (rc < 0) {
204                 eprintk(KERN_ERR,
205                         "v9fs_trans_tcp: problem connecting socket to %s\n",
206                         addr);
207                 return rc;
208         }
209         csocket->sk->sk_allocation = GFP_NOIO;
210
211         fd = sock_map_fd(csocket);
212         if (fd < 0) {
213                 sock_release(csocket);
214                 kfree(ts);
215                 trans->priv = NULL;
216                 return fd;
217         }
218
219         ts->s = csocket;
220         ts->filp = fget(fd);
221         ts->filp->f_flags |= O_NONBLOCK;
222         trans->status = Connected;
223
224         return 0;
225 }
226
227 /**
228  * v9fs_unix_init - initialize UNIX domain socket
229  * @v9ses: session information
230  * @dev_name: path to named pipe
231  * @data: mount options
232  *
233  */
234
235 static int
236 v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name,
237                char *data)
238 {
239         int rc, fd;
240         struct socket *csocket;
241         struct sockaddr_un sun_server;
242         struct v9fs_transport *trans;
243         struct v9fs_trans_sock *ts;
244
245         rc = 0;
246         csocket = NULL;
247         trans = v9ses->transport;
248
249         trans->status = Disconnected;
250
251         if (strlen(dev_name) > UNIX_PATH_MAX) {
252                 eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n",
253                         dev_name);
254                 return -ENOMEM;
255         }
256
257         ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
258         if (!ts)
259                 return -ENOMEM;
260
261         trans->priv = ts;
262         ts->s = NULL;
263         ts->filp = NULL;
264
265         sun_server.sun_family = PF_UNIX;
266         strcpy(sun_server.sun_path, dev_name);
267         sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
268         rc = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
269                 sizeof(struct sockaddr_un) - 1, 0);     /* -1 *is* important */
270         if (rc < 0) {
271                 eprintk(KERN_ERR,
272                         "v9fs_trans_unix: problem connecting socket: %s: %d\n",
273                         dev_name, rc);
274                 return rc;
275         }
276         csocket->sk->sk_allocation = GFP_NOIO;
277
278         fd = sock_map_fd(csocket);
279         if (fd < 0) {
280                 sock_release(csocket);
281                 kfree(ts);
282                 trans->priv = NULL;
283                 return fd;
284         }
285
286         ts->s = csocket;
287         ts->filp = fget(fd);
288         ts->filp->f_flags |= O_NONBLOCK;
289         trans->status = Connected;
290
291         return 0;
292 }
293
294 /**
295  * v9fs_sock_close - shutdown socket
296  * @trans: private socket structure
297  *
298  */
299
300 static void v9fs_sock_close(struct v9fs_transport *trans)
301 {
302         struct v9fs_trans_sock *ts;
303
304         if (!trans)
305                 return;
306
307         ts = trans->priv;
308
309         if ((ts) && (ts->filp)) {
310                 fput(ts->filp);
311                 ts->filp = NULL;
312                 ts->s = NULL;
313                 trans->status = Disconnected;
314         }
315
316         kfree(ts);
317
318         trans->priv = NULL;
319 }
320
321 struct v9fs_transport v9fs_trans_tcp = {
322         .init = v9fs_tcp_init,
323         .write = v9fs_sock_send,
324         .read = v9fs_sock_recv,
325         .close = v9fs_sock_close,
326         .poll = v9fs_sock_poll,
327 };
328
329 struct v9fs_transport v9fs_trans_unix = {
330         .init = v9fs_unix_init,
331         .write = v9fs_sock_send,
332         .read = v9fs_sock_recv,
333         .close = v9fs_sock_close,
334         .poll = v9fs_sock_poll,
335 };