Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mfashe...
[linux-2.6] / net / 9p / trans_fd.c
1 /*
2  * linux/fs/9p/trans_fd.c
3  *
4  * Fd transport layer.  Includes deprecated socket layer.
5  *
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>
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 version 2
13  *  as published by the Free Software Foundation.
14  *
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.
19  *
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
25  *
26  */
27
28 #include <linux/in.h>
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>
34 #include <linux/un.h>
35 #include <linux/uaccess.h>
36 #include <linux/inet.h>
37 #include <linux/idr.h>
38 #include <linux/file.h>
39 #include <net/9p/9p.h>
40 #include <net/9p/transport.h>
41
42 #define P9_PORT 564
43
44 struct p9_trans_fd {
45         struct file *rd;
46         struct file *wr;
47 };
48
49 static int p9_socket_open(struct p9_transport *trans, struct socket *csocket);
50 static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd);
51 static int p9_fd_read(struct p9_transport *trans, void *v, int len);
52 static int p9_fd_write(struct p9_transport *trans, void *v, int len);
53 static unsigned int p9_fd_poll(struct p9_transport *trans,
54                                                 struct poll_table_struct *pt);
55 static void p9_fd_close(struct p9_transport *trans);
56
57 struct p9_transport *p9_trans_create_tcp(const char *addr, int port)
58 {
59         int err;
60         struct p9_transport *trans;
61         struct socket *csocket;
62         struct sockaddr_in sin_server;
63
64         csocket = NULL;
65         trans = kmalloc(sizeof(struct p9_transport), GFP_KERNEL);
66         if (!trans)
67                 return ERR_PTR(-ENOMEM);
68
69         trans->write = p9_fd_write;
70         trans->read = p9_fd_read;
71         trans->close = p9_fd_close;
72         trans->poll = p9_fd_poll;
73
74         sin_server.sin_family = AF_INET;
75         sin_server.sin_addr.s_addr = in_aton(addr);
76         sin_server.sin_port = htons(port);
77         sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
78
79         if (!csocket) {
80                 P9_EPRINTK(KERN_ERR, "p9_trans_tcp: problem creating socket\n");
81                 err = -EIO;
82                 goto error;
83         }
84
85         err = csocket->ops->connect(csocket,
86                                     (struct sockaddr *)&sin_server,
87                                     sizeof(struct sockaddr_in), 0);
88         if (err < 0) {
89                 P9_EPRINTK(KERN_ERR,
90                         "p9_trans_tcp: problem connecting socket to %s\n",
91                         addr);
92                 goto error;
93         }
94
95         err = p9_socket_open(trans, csocket);
96         if (err < 0)
97                 goto error;
98
99         return trans;
100
101 error:
102         if (csocket)
103                 sock_release(csocket);
104
105         kfree(trans);
106         return ERR_PTR(err);
107 }
108 EXPORT_SYMBOL(p9_trans_create_tcp);
109
110 struct p9_transport *p9_trans_create_unix(const char *addr)
111 {
112         int err;
113         struct socket *csocket;
114         struct sockaddr_un sun_server;
115         struct p9_transport *trans;
116
117         csocket = NULL;
118         trans = kmalloc(sizeof(struct p9_transport), GFP_KERNEL);
119         if (!trans)
120                 return ERR_PTR(-ENOMEM);
121
122         trans->write = p9_fd_write;
123         trans->read = p9_fd_read;
124         trans->close = p9_fd_close;
125         trans->poll = p9_fd_poll;
126
127         if (strlen(addr) > UNIX_PATH_MAX) {
128                 P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
129                         addr);
130                 err = -ENAMETOOLONG;
131                 goto error;
132         }
133
134         sun_server.sun_family = PF_UNIX;
135         strcpy(sun_server.sun_path, addr);
136         sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
137         err = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
138                         sizeof(struct sockaddr_un) - 1, 0);
139         if (err < 0) {
140                 P9_EPRINTK(KERN_ERR,
141                         "p9_trans_unix: problem connecting socket: %s: %d\n",
142                         addr, err);
143                 goto error;
144         }
145
146         err = p9_socket_open(trans, csocket);
147         if (err < 0)
148                 goto error;
149
150         return trans;
151
152 error:
153         if (csocket)
154                 sock_release(csocket);
155
156         kfree(trans);
157         return ERR_PTR(err);
158 }
159 EXPORT_SYMBOL(p9_trans_create_unix);
160
161 struct p9_transport *p9_trans_create_fd(int rfd, int wfd)
162 {
163         int err;
164         struct p9_transport *trans;
165
166         if (rfd == ~0 || wfd == ~0) {
167                 printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
168                 return ERR_PTR(-ENOPROTOOPT);
169         }
170
171         trans = kmalloc(sizeof(struct p9_transport), GFP_KERNEL);
172         if (!trans)
173                 return ERR_PTR(-ENOMEM);
174
175         trans->write = p9_fd_write;
176         trans->read = p9_fd_read;
177         trans->close = p9_fd_close;
178         trans->poll = p9_fd_poll;
179
180         err = p9_fd_open(trans, rfd, wfd);
181         if (err < 0)
182                 goto error;
183
184         return trans;
185
186 error:
187         kfree(trans);
188         return ERR_PTR(err);
189 }
190 EXPORT_SYMBOL(p9_trans_create_fd);
191
192 static int p9_socket_open(struct p9_transport *trans, struct socket *csocket)
193 {
194         int fd, ret;
195
196         csocket->sk->sk_allocation = GFP_NOIO;
197         fd = sock_map_fd(csocket);
198         if (fd < 0) {
199                 P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to map fd\n");
200                 return fd;
201         }
202
203         ret = p9_fd_open(trans, fd, fd);
204         if (ret < 0) {
205                 P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
206                 sockfd_put(csocket);
207                 return ret;
208         }
209
210         ((struct p9_trans_fd *)trans->priv)->rd->f_flags |= O_NONBLOCK;
211
212         return 0;
213 }
214
215 static int p9_fd_open(struct p9_transport *trans, int rfd, int wfd)
216 {
217         struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd),
218                                            GFP_KERNEL);
219         if (!ts)
220                 return -ENOMEM;
221
222         ts->rd = fget(rfd);
223         ts->wr = fget(wfd);
224         if (!ts->rd || !ts->wr) {
225                 if (ts->rd)
226                         fput(ts->rd);
227                 if (ts->wr)
228                         fput(ts->wr);
229                 kfree(ts);
230                 return -EIO;
231         }
232
233         trans->priv = ts;
234         trans->status = Connected;
235
236         return 0;
237 }
238
239 /**
240  * p9_fd_read- read from a fd
241  * @v9ses: session information
242  * @v: buffer to receive data into
243  * @len: size of receive buffer
244  *
245  */
246 static int p9_fd_read(struct p9_transport *trans, void *v, int len)
247 {
248         int ret;
249         struct p9_trans_fd *ts = NULL;
250
251         if (trans && trans->status != Disconnected)
252                 ts = trans->priv;
253
254         if (!ts)
255                 return -EREMOTEIO;
256
257         if (!(ts->rd->f_flags & O_NONBLOCK))
258                 P9_DPRINTK(P9_DEBUG_ERROR, "blocking read ...\n");
259
260         ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
261         if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
262                 trans->status = Disconnected;
263         return ret;
264 }
265
266 /**
267  * p9_fd_write - write to a socket
268  * @v9ses: session information
269  * @v: buffer to send data from
270  * @len: size of send buffer
271  *
272  */
273 static int p9_fd_write(struct p9_transport *trans, void *v, int len)
274 {
275         int ret;
276         mm_segment_t oldfs;
277         struct p9_trans_fd *ts = NULL;
278
279         if (trans && trans->status != Disconnected)
280                 ts = trans->priv;
281
282         if (!ts)
283                 return -EREMOTEIO;
284
285         if (!(ts->wr->f_flags & O_NONBLOCK))
286                 P9_DPRINTK(P9_DEBUG_ERROR, "blocking write ...\n");
287
288         oldfs = get_fs();
289         set_fs(get_ds());
290         /* The cast to a user pointer is valid due to the set_fs() */
291         ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos);
292         set_fs(oldfs);
293
294         if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
295                 trans->status = Disconnected;
296         return ret;
297 }
298
299 static unsigned int
300 p9_fd_poll(struct p9_transport *trans, struct poll_table_struct *pt)
301 {
302         int ret, n;
303         struct p9_trans_fd *ts = NULL;
304         mm_segment_t oldfs;
305
306         if (trans && trans->status == Connected)
307                 ts = trans->priv;
308
309         if (!ts)
310                 return -EREMOTEIO;
311
312         if (!ts->rd->f_op || !ts->rd->f_op->poll)
313                 return -EIO;
314
315         if (!ts->wr->f_op || !ts->wr->f_op->poll)
316                 return -EIO;
317
318         oldfs = get_fs();
319         set_fs(get_ds());
320
321         ret = ts->rd->f_op->poll(ts->rd, pt);
322         if (ret < 0)
323                 goto end;
324
325         if (ts->rd != ts->wr) {
326                 n = ts->wr->f_op->poll(ts->wr, pt);
327                 if (n < 0) {
328                         ret = n;
329                         goto end;
330                 }
331                 ret = (ret & ~POLLOUT) | (n & ~POLLIN);
332         }
333
334 end:
335         set_fs(oldfs);
336         return ret;
337 }
338
339 /**
340  * p9_sock_close - shutdown socket
341  * @trans: private socket structure
342  *
343  */
344 static void p9_fd_close(struct p9_transport *trans)
345 {
346         struct p9_trans_fd *ts;
347
348         if (!trans)
349                 return;
350
351         ts = xchg(&trans->priv, NULL);
352
353         if (!ts)
354                 return;
355
356         trans->status = Disconnected;
357         if (ts->rd)
358                 fput(ts->rd);
359         if (ts->wr)
360                 fput(ts->wr);
361         kfree(ts);
362 }
363