xref: /illumos-gate/usr/src/uts/common/syscall/strcalls.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #include <sys/types.h>
31 #include <sys/sysmacros.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/errno.h>
35 #include <sys/vnode.h>
36 #include <sys/file.h>
37 #include <sys/proc.h>
38 #include <sys/stropts.h>
39 #include <sys/stream.h>
40 #include <sys/strsubr.h>
41 #include <sys/fs/fifonode.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/debug.h>
45 
46 /*
47  * STREAMS system calls.
48  */
49 
50 int getmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *flagsp);
51 int putmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int flags);
52 int getpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *prip,
53     int *flagsp);
54 int putpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int pri,
55     int flags);
56 
57 static int msgio(int fdes, struct strbuf *ctl, struct strbuf *data, int *rval,
58     int mode, unsigned char *prip, int *flagsp);
59 
60 int
61 getmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *flagsp)
62 {
63 	int error;
64 	int localflags;
65 	int realflags = 0;
66 	unsigned char pri = 0;
67 	int rv = 0;
68 
69 	/*
70 	 * Convert between old flags (localflags) and new flags (realflags).
71 	 */
72 	if (copyin(flagsp, &localflags, sizeof (*flagsp)))
73 		return (set_errno(EFAULT));
74 	switch (localflags) {
75 	case 0:
76 		realflags = MSG_ANY;
77 		break;
78 
79 	case RS_HIPRI:
80 		realflags = MSG_HIPRI;
81 		break;
82 
83 	default:
84 		return (set_errno(EINVAL));
85 	}
86 
87 	if ((error = msgio(fdes, ctl, data, &rv, FREAD, &pri,
88 	    &realflags)) == 0) {
89 		/*
90 		 * massage realflags based on localflags.
91 		 */
92 		if (realflags == MSG_HIPRI)
93 			localflags = RS_HIPRI;
94 		else
95 			localflags = 0;
96 		if (copyout(&localflags, flagsp, sizeof (*flagsp)))
97 			error = EFAULT;
98 	}
99 	if (error != 0)
100 		return (set_errno(error));
101 	return (rv);
102 }
103 
104 int
105 putmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int flags)
106 {
107 	unsigned char pri = 0;
108 	int realflags;
109 	int error;
110 	int rv = 0;
111 
112 	switch (flags) {
113 	case RS_HIPRI:
114 		realflags = MSG_HIPRI;
115 		break;
116 	case (RS_HIPRI|MSG_XPG4):
117 		realflags = MSG_HIPRI|MSG_XPG4;
118 		break;
119 	case MSG_XPG4:
120 		realflags = MSG_BAND|MSG_XPG4;
121 		break;
122 	case 0:
123 		realflags = MSG_BAND;
124 		break;
125 
126 	default:
127 		return (set_errno(EINVAL));
128 	}
129 	error = msgio(fdes, ctl, data, &rv, FWRITE, &pri, &realflags);
130 	if (error != 0)
131 		return (set_errno(error));
132 	return (rv);
133 }
134 
135 
136 int
137 getpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int *prip,
138     int *flagsp)
139 {
140 	int error;
141 	int flags;
142 	int intpri;
143 	unsigned char pri;
144 	int rv = 0;
145 
146 	if (copyin(flagsp, &flags, sizeof (flags)))
147 		return (set_errno(EFAULT));
148 	if (copyin(prip, &intpri, sizeof (intpri)))
149 		return (set_errno(EFAULT));
150 	if ((intpri > 255) || (intpri < 0))
151 		return (set_errno(EINVAL));
152 	pri = (unsigned char)intpri;
153 	error = msgio(fdes, ctl, data, &rv, FREAD, &pri, &flags);
154 	if (error != 0)
155 		return (set_errno(error));
156 	if (copyout(&flags, flagsp, sizeof (flags)))
157 		return (set_errno(EFAULT));
158 	intpri = (int)pri;
159 	if (copyout(&intpri, prip, sizeof (intpri)))
160 		return (set_errno(EFAULT));
161 	return (rv);
162 }
163 
164 int
165 putpmsg(int fdes, struct strbuf *ctl, struct strbuf *data, int intpri,
166     int flags)
167 {
168 	unsigned char pri;
169 	int rv = 0;
170 	int error;
171 
172 	if ((intpri > 255) || (intpri < 0))
173 		return (set_errno(EINVAL));
174 	pri = (unsigned char)intpri;
175 	error = msgio(fdes, ctl, data, &rv, FWRITE, &pri, &flags);
176 	if (error != 0)
177 		return (set_errno(error));
178 	return (rv);
179 }
180 
181 /*
182  * Common code for getmsg and putmsg calls: check permissions,
183  * copy in args, do preliminary setup, and switch to
184  * appropriate stream routine.
185  */
186 static int
187 msgio(int fdes, struct strbuf *ctl, struct strbuf *data, int *rval,
188     int mode, unsigned char *prip, int *flagsp)
189 {
190 	file_t *fp;
191 	vnode_t *vp;
192 	struct strbuf msgctl, msgdata;
193 	int error;
194 	int flag;
195 	klwp_t *lwp = ttolwp(curthread);
196 	rval_t rv;
197 
198 	if ((fp = getf(fdes)) == NULL)
199 		return (EBADF);
200 	if ((fp->f_flag & mode) == 0) {
201 		releasef(fdes);
202 		return (EBADF);
203 	}
204 	vp = fp->f_vnode;
205 	if (vp->v_type == VFIFO) {
206 		if (vp->v_stream) {
207 			/*
208 			 * must use sd_vnode, could be named pipe
209 			 */
210 			(void) fifo_vfastoff(vp->v_stream->sd_vnode);
211 		} else {
212 			releasef(fdes);
213 			return (ENOSTR);
214 		}
215 	} else if ((vp->v_type != VCHR && vp->v_type != VSOCK) ||
216 		    vp->v_stream == NULL) {
217 		releasef(fdes);
218 		return (ENOSTR);
219 	}
220 	if ((ctl != NULL) &&
221 	    copyin(ctl, &msgctl, sizeof (struct strbuf))) {
222 		releasef(fdes);
223 		return (EFAULT);
224 	}
225 	if ((data != NULL) &&
226 	    copyin(data, &msgdata, sizeof (struct strbuf))) {
227 		releasef(fdes);
228 		return (EFAULT);
229 	}
230 
231 	if (mode == FREAD) {
232 		if (ctl == NULL)
233 			msgctl.maxlen = -1;
234 		if (data == NULL)
235 			msgdata.maxlen = -1;
236 		flag = fp->f_flag;
237 		rv.r_val1 = 0;
238 		if (vp->v_type == VSOCK) {
239 			error = sock_getmsg(vp, &msgctl, &msgdata, prip,
240 			    flagsp, flag, &rv);
241 		} else {
242 			error = strgetmsg(vp, &msgctl, &msgdata, prip,
243 			    flagsp, flag, &rv);
244 		}
245 		*rval = rv.r_val1;
246 		if (error != 0) {
247 			releasef(fdes);
248 			return (error);
249 		}
250 		if (lwp != NULL)
251 			lwp->lwp_ru.msgrcv++;
252 		if (((ctl != NULL) &&
253 		    copyout(&msgctl, ctl, sizeof (struct strbuf))) ||
254 		    ((data != NULL) &&
255 		    copyout(&msgdata, data, sizeof (struct strbuf)))) {
256 			releasef(fdes);
257 			return (EFAULT);
258 		}
259 		releasef(fdes);
260 		return (0);
261 	}
262 
263 	/*
264 	 * FWRITE case
265 	 */
266 	if (ctl == NULL)
267 		msgctl.len = -1;
268 	if (data == NULL)
269 		msgdata.len = -1;
270 	flag = fp->f_flag;
271 	if (vp->v_type == VSOCK) {
272 		error = sock_putmsg(vp, &msgctl, &msgdata, *prip, *flagsp,
273 		    flag);
274 	} else {
275 		error = strputmsg(vp, &msgctl, &msgdata, *prip, *flagsp, flag);
276 	}
277 	releasef(fdes);
278 	if (error == 0 && lwp != NULL)
279 		lwp->lwp_ru.msgsnd++;
280 	return (error);
281 }
282 
283 
284 #if defined(_LP64) && defined(_SYSCALL32)
285 
286 static int msgio32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data,
287     int *rval, int mode, unsigned char *prip, int *flagsp);
288 
289 int
290 getmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t *flagsp)
291 {
292 	int error;
293 	int32_t localflags;
294 	int realflags = 0;
295 	unsigned char pri = 0;
296 	int rv = 0;
297 
298 	/*
299 	 * Convert between old flags (localflags) and new flags (realflags).
300 	 */
301 	if (copyin(flagsp, &localflags, sizeof (*flagsp)))
302 		return (set_errno(EFAULT));
303 	switch (localflags) {
304 	case 0:
305 		realflags = MSG_ANY;
306 		break;
307 
308 	case RS_HIPRI:
309 		realflags = MSG_HIPRI;
310 		break;
311 
312 	default:
313 		return (set_errno(EINVAL));
314 	}
315 
316 	if ((error = msgio32(fdes, ctl, data, &rv, FREAD, &pri,
317 	    &realflags)) == 0) {
318 		/*
319 		 * massage realflags based on localflags.
320 		 */
321 		if (realflags == MSG_HIPRI)
322 			localflags = RS_HIPRI;
323 		else
324 			localflags = 0;
325 		if (copyout(&localflags, flagsp, sizeof (*flagsp)))
326 			error = EFAULT;
327 	}
328 	if (error != 0)
329 		return (set_errno(error));
330 	return (rv);
331 }
332 
333 int
334 putmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t flags)
335 {
336 	unsigned char pri = 0;
337 	int realflags;
338 	int error;
339 	int rv = 0;
340 
341 	switch (flags) {
342 	case RS_HIPRI:
343 		realflags = MSG_HIPRI;
344 		break;
345 	case (RS_HIPRI|MSG_XPG4):
346 		realflags = MSG_HIPRI|MSG_XPG4;
347 		break;
348 	case MSG_XPG4:
349 		realflags = MSG_BAND|MSG_XPG4;
350 		break;
351 	case 0:
352 		realflags = MSG_BAND;
353 		break;
354 
355 	default:
356 		return (set_errno(EINVAL));
357 	}
358 	error = msgio32(fdes, ctl, data, &rv, FWRITE, &pri, &realflags);
359 	if (error != 0)
360 		return (set_errno(error));
361 	return (rv);
362 }
363 
364 
365 int
366 getpmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t *prip,
367     int32_t *flagsp)
368 {
369 	int error;
370 	int32_t flags;
371 	int32_t intpri;
372 	unsigned char pri;
373 	int rv = 0;
374 
375 	if (copyin(flagsp, &flags, sizeof (*flagsp)))
376 		return (set_errno(EFAULT));
377 	if (copyin(prip, &intpri, sizeof (intpri)))
378 		return (set_errno(EFAULT));
379 	if ((intpri > 255) || (intpri < 0))
380 		return (set_errno(EINVAL));
381 	pri = (unsigned char)intpri;
382 	error = msgio32(fdes, ctl, data, &rv, FREAD, &pri, &flags);
383 	if (error != 0)
384 		return (set_errno(error));
385 	if (copyout(&flags, flagsp, sizeof (flags)))
386 		return (set_errno(EFAULT));
387 	intpri = (int)pri;
388 	if (copyout(&intpri, prip, sizeof (intpri)))
389 		return (set_errno(EFAULT));
390 	return (rv);
391 }
392 
393 int
394 putpmsg32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int32_t intpri,
395     int32_t flags)
396 {
397 	unsigned char pri;
398 	int rv = 0;
399 	int error;
400 
401 	if ((intpri > 255) || (intpri < 0))
402 		return (set_errno(EINVAL));
403 	pri = (unsigned char)intpri;
404 	error = msgio32(fdes, ctl, data, &rv, FWRITE, &pri, &flags);
405 	if (error != 0)
406 		return (set_errno(error));
407 	return (rv);
408 }
409 
410 /*
411  * Common code for getmsg and putmsg calls: check permissions,
412  * copy in args, do preliminary setup, and switch to
413  * appropriate stream routine.
414  */
415 static int
416 msgio32(int fdes, struct strbuf32 *ctl, struct strbuf32 *data, int *rval,
417     int mode, unsigned char *prip, int *flagsp)
418 {
419 	file_t *fp;
420 	vnode_t *vp;
421 	struct strbuf32 msgctl32, msgdata32;
422 	struct strbuf msgctl, msgdata;
423 	int error;
424 	int flag;
425 	klwp_t *lwp = ttolwp(curthread);
426 	rval_t rv;
427 
428 	if ((fp = getf(fdes)) == NULL)
429 		return (EBADF);
430 	if ((fp->f_flag & mode) == 0) {
431 		releasef(fdes);
432 		return (EBADF);
433 	}
434 	vp = fp->f_vnode;
435 	if (vp->v_type == VFIFO) {
436 		if (vp->v_stream) {
437 			/*
438 			 * must use sd_vnode, could be named pipe
439 			 */
440 			(void) fifo_vfastoff(vp->v_stream->sd_vnode);
441 		} else {
442 			releasef(fdes);
443 			return (ENOSTR);
444 		}
445 	} else if ((vp->v_type != VCHR && vp->v_type != VSOCK) ||
446 		    vp->v_stream == NULL) {
447 		releasef(fdes);
448 		return (ENOSTR);
449 	}
450 	if (ctl != NULL) {
451 		if (copyin(ctl, &msgctl32, sizeof (msgctl32))) {
452 			releasef(fdes);
453 			return (EFAULT);
454 		}
455 		msgctl.len = msgctl32.len;
456 		msgctl.maxlen = msgctl32.maxlen;
457 		msgctl.buf = (caddr_t)(uintptr_t)msgctl32.buf;
458 	}
459 	if (data != NULL) {
460 		if (copyin(data, &msgdata32, sizeof (msgdata32))) {
461 			releasef(fdes);
462 			return (EFAULT);
463 		}
464 		msgdata.len = msgdata32.len;
465 		msgdata.maxlen = msgdata32.maxlen;
466 		msgdata.buf = (caddr_t)(uintptr_t)msgdata32.buf;
467 	}
468 
469 	if (mode == FREAD) {
470 		if (ctl == NULL)
471 			msgctl.maxlen = -1;
472 		if (data == NULL)
473 			msgdata.maxlen = -1;
474 		flag = fp->f_flag;
475 		rv.r_val1 = 0;
476 		if (vp->v_type == VSOCK) {
477 			error = sock_getmsg(vp, &msgctl, &msgdata, prip,
478 			    flagsp, flag, &rv);
479 		} else {
480 			error = strgetmsg(vp, &msgctl, &msgdata, prip,
481 			    flagsp, flag, &rv);
482 		}
483 		*rval = rv.r_val1;
484 		if (error != 0) {
485 			releasef(fdes);
486 			return (error);
487 		}
488 		if (lwp != NULL)
489 			lwp->lwp_ru.msgrcv++;
490 		if (ctl != NULL) {
491 			/* XX64 - range check */
492 			msgctl32.len = msgctl.len;
493 			msgctl32.maxlen = msgctl.maxlen;
494 			msgctl32.buf = (caddr32_t)(uintptr_t)msgctl.buf;
495 			if (copyout(&msgctl32, ctl, sizeof (msgctl32))) {
496 				releasef(fdes);
497 				return (EFAULT);
498 			}
499 		}
500 		if (data != NULL) {
501 			/* XX64 - range check */
502 			msgdata32.len = msgdata.len;
503 			msgdata32.maxlen = msgdata.maxlen;
504 			msgdata32.buf = (caddr32_t)(uintptr_t)msgdata.buf;
505 			if (copyout(&msgdata32, data, sizeof (msgdata32))) {
506 				releasef(fdes);
507 				return (EFAULT);
508 			}
509 		}
510 		releasef(fdes);
511 		return (0);
512 	}
513 
514 	/*
515 	 * FWRITE case
516 	 */
517 	if (ctl == NULL)
518 		msgctl.len = -1;
519 	if (data == NULL)
520 		msgdata.len = -1;
521 	flag = fp->f_flag;
522 	if (vp->v_type == VSOCK) {
523 		error = sock_putmsg(vp, &msgctl, &msgdata, *prip, *flagsp,
524 		    flag);
525 	} else {
526 		error = strputmsg(vp, &msgctl, &msgdata, *prip, *flagsp, flag);
527 	}
528 	releasef(fdes);
529 	if (error == 0 && lwp != NULL)
530 		lwp->lwp_ru.msgsnd++;
531 	return (error);
532 }
533 
534 #endif /* _LP64 && _SYSCALL32 */
535