xref: /openbmc/linux/net/9p/trans_fd.c (revision c21b37f6)
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 
364