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