This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-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         oldfs = get_fs();
114         set_fs(get_ds());
115         ret = vfs_write(ts->filp, (void __user *)v, len, &ts->filp->f_pos);
116         set_fs(oldfs);
117
118         if (ret < 0) {
119                 if (ret != -ERESTARTSYS)
120                         trans->status = Disconnected;
121         }
122
123         return ret;
124 }
125
126 static unsigned int v9fs_sock_poll(struct v9fs_transport *trans,
127         struct poll_table_struct *pt) {
128
129         int ret;
130         struct v9fs_trans_sock *ts;
131         mm_segment_t oldfs;
132
133         if (!trans) {
134                 dprintk(DEBUG_ERROR, "no transport\n");
135                 return -EIO;
136         }
137
138         ts = trans->priv;
139         if (trans->status != Connected || !ts) {
140                 dprintk(DEBUG_ERROR, "transport disconnected: %d\n", trans->status);
141                 return -EIO;
142         }
143
144         oldfs = get_fs();
145         set_fs(get_ds());
146
147         if (!ts->filp->f_op || !ts->filp->f_op->poll) {
148                 dprintk(DEBUG_ERROR, "no poll operation\n");
149                 ret = -EIO;
150                 goto end;
151         }
152
153         ret = ts->filp->f_op->poll(ts->filp, pt);
154
155 end:
156         set_fs(oldfs);
157         return ret;
158 }
159
160
161 /**
162  * v9fs_tcp_init - initialize TCP socket
163  * @v9ses: session information
164  * @addr: address of server to mount
165  * @data: mount options
166  *
167  */
168
169 static int
170 v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data)
171 {
172         struct socket *csocket = NULL;
173         struct sockaddr_in sin_server;
174         int rc = 0;
175         struct v9fs_trans_sock *ts = NULL;
176         struct v9fs_transport *trans = v9ses->transport;
177         int fd;
178
179         trans->status = Disconnected;
180
181         ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
182
183         if (!ts)
184                 return -ENOMEM;
185
186         trans->priv = ts;
187         ts->s = NULL;
188         ts->filp = NULL;
189
190         if (!addr)
191                 return -EINVAL;
192
193         dprintk(DEBUG_TRANS, "Connecting to %s\n", addr);
194
195         sin_server.sin_family = AF_INET;
196         sin_server.sin_addr.s_addr = in_aton(addr);
197         sin_server.sin_port = htons(v9ses->port);
198         sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
199         rc = csocket->ops->connect(csocket,
200                                    (struct sockaddr *)&sin_server,
201                                    sizeof(struct sockaddr_in), 0);
202         if (rc < 0) {
203                 eprintk(KERN_ERR,
204                         "v9fs_trans_tcp: problem connecting socket to %s\n",
205                         addr);
206                 return rc;
207         }
208         csocket->sk->sk_allocation = GFP_NOIO;
209
210         fd = sock_map_fd(csocket);
211         if (fd < 0) {
212                 sock_release(csocket);
213                 kfree(ts);
214                 trans->priv = NULL;
215                 return fd;
216         }
217
218         ts->s = csocket;
219         ts->filp = fget(fd);
220         ts->filp->f_flags |= O_NONBLOCK;
221         trans->status = Connected;
222
223         return 0;
224 }
225
226 /**
227  * v9fs_unix_init - initialize UNIX domain socket
228  * @v9ses: session information
229  * @dev_name: path to named pipe
230  * @data: mount options
231  *
232  */
233
234 static int
235 v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name,
236                char *data)
237 {
238         int rc, fd;
239         struct socket *csocket;
240         struct sockaddr_un sun_server;
241         struct v9fs_transport *trans;
242         struct v9fs_trans_sock *ts;
243
244         rc = 0;
245         csocket = NULL;
246         trans = v9ses->transport;
247
248         trans->status = Disconnected;
249
250         if (strlen(dev_name) > UNIX_PATH_MAX) {
251                 eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n",
252                         dev_name);
253                 return -ENOMEM;
254         }
255
256         ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
257         if (!ts)
258                 return -ENOMEM;
259
260         trans->priv = ts;
261         ts->s = NULL;
262         ts->filp = NULL;
263
264         sun_server.sun_family = PF_UNIX;
265         strcpy(sun_server.sun_path, dev_name);
266         sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
267         rc = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
268                 sizeof(struct sockaddr_un) - 1, 0);     /* -1 *is* important */
269         if (rc < 0) {
270                 eprintk(KERN_ERR,
271                         "v9fs_trans_unix: problem connecting socket: %s: %d\n",
272                         dev_name, rc);
273                 return rc;
274         }
275         csocket->sk->sk_allocation = GFP_NOIO;
276
277         fd = sock_map_fd(csocket);
278         if (fd < 0) {
279                 sock_release(csocket);
280                 kfree(ts);
281                 trans->priv = NULL;
282                 return fd;
283         }
284
285         ts->s = csocket;
286         ts->filp = fget(fd);
287         ts->filp->f_flags |= O_NONBLOCK;
288         trans->status = Connected;
289
290         return 0;
291 }
292
293 /**
294  * v9fs_sock_close - shutdown socket
295  * @trans: private socket structure
296  *
297  */
298
299 static void v9fs_sock_close(struct v9fs_transport *trans)
300 {
301         struct v9fs_trans_sock *ts;
302
303         if (!trans)
304                 return;
305
306         ts = trans->priv;
307
308         if ((ts) && (ts->filp)) {
309                 fput(ts->filp);
310                 ts->filp = NULL;
311                 ts->s = NULL;
312                 trans->status = Disconnected;
313         }
314
315         kfree(ts);
316
317         trans->priv = NULL;
318 }
319
320 struct v9fs_transport v9fs_trans_tcp = {
321         .init = v9fs_tcp_init,
322         .write = v9fs_sock_send,
323         .read = v9fs_sock_recv,
324         .close = v9fs_sock_close,
325         .poll = v9fs_sock_poll,
326 };
327
328 struct v9fs_transport v9fs_trans_unix = {
329         .init = v9fs_unix_init,
330         .write = v9fs_sock_send,
331         .read = v9fs_sock_recv,
332         .close = v9fs_sock_close,
333         .poll = v9fs_sock_poll,
334 };