xref: /csrg-svn/sys/sparc/sunos/sun_misc.c (revision 55099)
1 /*
2  * Copyright (c) 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This software was developed by the Computer Systems Engineering group
6  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7  * contributed to Berkeley.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)sun_misc.c	7.1 (Berkeley) 07/13/92
12  *
13  * from: $Header: sun_misc.c,v 1.12 92/07/12 13:26:10 torek Exp $
14  */
15 
16 /*
17  * SunOS compatibility module.
18  *
19  * SunOS system calls that are implemented differently in BSD are
20  * handled here.
21  */
22 
23 #include "param.h"
24 #include "proc.h"
25 #include "file.h"
26 #include "filedesc.h"
27 #include "ioctl.h"
28 #include "malloc.h"
29 #include "mbuf.h"
30 #include "mman.h"
31 #include "mount.h"
32 #include "resource.h"
33 #include "resourcevar.h"
34 #include "signal.h"
35 #include "signalvar.h"
36 #include "socket.h"
37 #include "vnode.h"
38 #include "specdev.h"
39 #include "uio.h"
40 #include "wait.h"
41 
42 #include "vm/vm.h"
43 
44 struct sun_wait4_args {
45 	int	pid;
46 	int	*status;
47 	int	options;
48 	struct	rusage *rusage;
49 };
50 sun_wait4(p, uap, retval)
51 	struct proc *p;
52 	struct sun_wait4_args *uap;
53 	int *retval;
54 {
55 
56 	if (uap->pid == 0)
57 		uap->pid = WAIT_ANY;
58 	return (wait4(p, uap, retval));
59 }
60 
61 struct sun_creat_args {
62 	char	*fname;
63 	int	fmode;
64 };
65 sun_creat(p, uap, retval)
66 	struct proc *p;
67 	struct sun_creat_args *uap;
68 	int *retval;
69 {
70 	struct args {
71 		char	*fname;
72 		int	mode;
73 		int	crtmode;
74 	} openuap;
75 
76 	openuap.fname = uap->fname;
77 	openuap.crtmode = uap->fmode;
78 	openuap.mode = O_WRONLY | O_CREAT | O_TRUNC;
79 	return (open(p, &openuap, retval));
80 }
81 
82 struct sun_execv_args {
83 	char	*fname;
84 	char	**argp;
85 	char	**envp;		/* pseudo */
86 };
87 sun_execv(p, uap, retval)
88 	struct proc *p;
89 	struct sun_execv_args *uap;
90 	int *retval;
91 {
92 
93 	uap->envp = NULL;
94 	return (execve(p, uap, retval));
95 }
96 
97 struct sun_omsync_args {
98 	caddr_t	addr;
99 	int	len;
100 	int	flags;
101 };
102 sun_omsync(p, uap, retval)
103 	struct proc *p;
104 	struct sun_omsync_args *uap;
105 	int *retval;
106 {
107 
108 	if (uap->flags)
109 		return (EINVAL);
110 	return (msync(p, uap, retval));
111 }
112 
113 struct sun_unmount_args {
114 	char	*name;
115 	int	flags;	/* pseudo */
116 };
117 sun_unmount(p, uap, retval)
118 	struct proc *p;
119 	struct sun_unmount_args *uap;
120 	int *retval;
121 {
122 
123 	uap->flags = MNT_NOFORCE;
124 	return (unmount(p, uap, retval));
125 }
126 
127 static int
128 gettype(tptr)
129 	int *tptr;
130 {
131 	int type, error;
132 	char in[20];
133 
134 	if (error = copyinstr((caddr_t)*tptr, in, sizeof in, (u_int *)0))
135 		return (error);
136 	if (strcmp(in, "4.2") == 0 || strcmp(in, "ufs") == 0)
137 		type = MOUNT_UFS;
138 	else if (strcmp(in, "nfs") == 0)
139 		type = MOUNT_NFS;
140 	else
141 		return (EINVAL);
142 	*tptr = type;
143 	return (0);
144 }
145 
146 #define	SUNM_RDONLY	0x01	/* mount fs read-only */
147 #define	SUNM_NOSUID	0x02	/* mount fs with setuid disallowed */
148 #define	SUNM_NEWTYPE	0x04	/* type is string (char *), not int */
149 #define	SUNM_GRPID	0x08	/* (bsd semantics; ignored) */
150 #define	SUNM_REMOUNT	0x10	/* update existing mount */
151 #define	SUNM_NOSUB	0x20	/* prevent submounts (rejected) */
152 #define	SUNM_MULTI	0x40	/* (ignored) */
153 #define	SUNM_SYS5	0x80	/* Sys 5-specific semantics (rejected) */
154 
155 struct sun_mount_args {
156 	int	type;
157 	char	*dir;
158 	int	flags;
159 	caddr_t	data;
160 };
161 sun_mount(p, uap, retval)
162 	struct proc *p;
163 	struct sun_mount_args *uap;
164 	int *retval;
165 {
166 	int oflags = uap->flags, nflags, error;
167 
168 	if (oflags & (SUNM_NOSUB | SUNM_SYS5))
169 		return (EINVAL);
170 	if (oflags & SUNM_NEWTYPE && (error = gettype(&uap->type)))
171 		return (error);
172 	nflags = 0;
173 	if (oflags & SUNM_RDONLY)
174 		nflags |= MNT_RDONLY;
175 	if (oflags & SUNM_NOSUID)
176 		nflags |= MNT_NOSUID;
177 	if (oflags & SUNM_REMOUNT)
178 		nflags |= MNT_UPDATE;
179 	uap->flags = nflags;
180 	return (mount(p, uap, retval));
181 }
182 
183 struct sun_sigpending_args {
184 	int	*mask;
185 };
186 sun_sigpending(p, uap, retval)
187 	struct proc *p;
188 	struct sun_sigpending_args *uap;
189 	int *retval;
190 {
191 	int mask = p->p_sig & p->p_sigmask;
192 
193 	return (copyout((caddr_t)&mask, (caddr_t)uap->mask, sizeof(int)));
194 }
195 
196 #if 0
197 /* here is the sun layout (not used directly): */
198 struct sun_dirent {
199 	long	d_off;
200 	u_long	d_fileno;
201 	u_short	d_reclen;
202 	u_short	d_namlen;
203 	char	d_name[256];
204 };
205 #endif
206 /* and the BSD layout: */
207 struct bsd_dirent {
208 	u_long	d_fileno;
209 	u_short	d_reclen;
210 	u_short	d_namlen;
211 	char	d_name[256];
212 };
213 
214 /*
215  * Read Sun-style directory entries.  We suck them into kernel space so
216  * that they can be massaged before being copied out to user code.  Like
217  * SunOS, we squish out `empty' entries.
218  *
219  * This is quite ugly, but what do you expect from compatibility code?
220  */
221 struct sun_getdents_args {
222 	int	fd;
223 	char	*buf;
224 	int	nbytes;
225 };
226 sun_getdents(p, uap, retval)
227 	struct proc *p;
228 	register struct sun_getdents_args *uap;
229 	int *retval;
230 {
231 	register struct vnode *vp;
232 	register caddr_t inp, buf;	/* BSD-format */
233 	register int len, reclen;	/* BSD-format */
234 	register caddr_t outp;		/* Sun-format */
235 	register int resid;		/* Sun-format */
236 	struct file *fp;
237 	struct uio auio;
238 	struct iovec aiov;
239 	off_t off;			/* true file offset */
240 	long soff;			/* Sun file offset */
241 	int buflen, error, eofflag;
242 #define	SUN_RECLEN(reclen) (reclen + sizeof(long))
243 
244 	if ((error = getvnode(p->p_fd, uap->fd, &fp)) != 0)
245 		return (error);
246 	if ((fp->f_flag & FREAD) == 0)
247 		return (EBADF);
248 	vp = (struct vnode *)fp->f_data;
249 	if (vp->v_type != VDIR)	/* XXX  vnode readdir op should do this */
250 		return (EINVAL);
251 	buflen = min(MAXBSIZE, uap->nbytes);
252 	buf = malloc(buflen, M_TEMP, M_WAITOK);
253 	VOP_LOCK(vp);
254 	off = fp->f_offset;
255 again:
256 	aiov.iov_base = buf;
257 	aiov.iov_len = buflen;
258 	auio.uio_iov = &aiov;
259 	auio.uio_iovcnt = 1;
260 	auio.uio_rw = UIO_READ;
261 	auio.uio_segflg = UIO_SYSSPACE;
262 	auio.uio_procp = p;
263 	auio.uio_resid = buflen;
264 	auio.uio_offset = off;
265 	/*
266 	 * First we read into the malloc'ed buffer, then
267 	 * we massage it into user space, one record at a time.
268 	 */
269 	if (error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag))
270 		goto out;
271 	inp = buf;
272 	outp = uap->buf;
273 	resid = uap->nbytes;
274 	if ((len = buflen - auio.uio_resid) == 0)
275 		goto eof;
276 	for (; len > 0; len -= reclen) {
277 		reclen = ((struct bsd_dirent *)inp)->d_reclen;
278 		if (reclen & 3)
279 			panic("sun_getdents");
280 		off += reclen;		/* each entry points to next */
281 		if (((struct bsd_dirent *)inp)->d_fileno == 0) {
282 			inp += reclen;	/* it is a hole; squish it out */
283 			continue;
284 		}
285 		if (reclen > len || resid < SUN_RECLEN(reclen)) {
286 			/* entry too big for buffer, so just stop */
287 			outp++;
288 			break;
289 		}
290 		/* copy out a Sun-shaped dirent */
291 		((struct bsd_dirent *)inp)->d_reclen = SUN_RECLEN(reclen);
292 		soff = off;
293 		if ((error = copyout((caddr_t)&soff, outp, sizeof soff)) != 0 ||
294 		    (error = copyout(inp, outp + sizeof soff, reclen)) != 0)
295 			goto out;
296 		/* advance past this real entry */
297 		inp += reclen;
298 		/* advance output past Sun-shaped entry */
299 		outp += SUN_RECLEN(reclen);
300 		resid -= SUN_RECLEN(reclen);
301 	}
302 	/* if we squished out the whole block, try again */
303 	if (outp == uap->buf)
304 		goto again;
305 	fp->f_offset = off;		/* update the vnode offset */
306 eof:
307 	*retval = uap->nbytes - resid;
308 out:
309 	VOP_UNLOCK(vp);
310 	free(buf, M_TEMP);
311 	return (error);
312 }
313 
314 #define	MAXDOMAINNAME	64
315 char	sun_domainname[MAXDOMAINNAME];
316 int	sun_domainnamelen = 1;
317 
318 struct sun_getdomainname_args {
319 	char	*name;
320 	int	namelen;
321 };
322 sun_getdomainname(p, uap, retval)
323 	struct proc *p;
324 	struct sun_getdomainname_args *uap;
325 	int *retval;
326 {
327 	register int l = min(uap->namelen, sun_domainnamelen + 1);
328 
329 	return (copyout(sun_domainname, uap->name, l));
330 }
331 
332 struct sun_setdomainname_args {
333 	char	*name;
334 	int	namelen;
335 };
336 sun_setdomainname(p, uap, retval)
337 	struct proc *p;
338 	struct sun_setdomainname_args *uap;
339 	int *retval;
340 {
341 	register int l = uap->namelen, error;
342 
343 	if (l >= MAXDOMAINNAME)
344 		return (EINVAL);	/* ??? ENAMETOOLONG? */
345 	if (error = suser(p->p_ucred, &p->p_acflag))
346 		return (error);
347 	if (error = copyin(uap->name, sun_domainname, l))
348 		return (error);
349 	sun_domainname[l] = 0;
350 	return (0);
351 }
352 
353 #define	SUN_MMAP_MASK	0xf		/* mask for SHARED/PRIVATE */
354 #define	SUN_MMAP_CANDO	0x80000000	/* if not, old mmap & cannot handle */
355 
356 #define	DEVZERO	makedev(3, 12)		/* major,minor of /dev/zero */
357 
358 #define	SUN_MMAP_SAME	(MAP_SHARED|MAP_PRIVATE|MAP_FIXED|MAP_INHERIT)
359 
360 struct sun_mmap_args {
361 	caddr_t	addr;
362 	size_t	len;
363 	int	prot;
364 	int	flags;
365 	int	fd;
366 	long	off;		/* not off_t! */
367 	quad_t	qoff;		/* created here and fed to smmap() */
368 };
369 sun_mmap(p, uap, retval)
370 	register struct proc *p;
371 	register struct sun_mmap_args *uap;
372 	int *retval;
373 {
374 	register int flags;
375 	register struct filedesc *fdp;
376 	register struct file *fp;
377 	register struct vnode *vp;
378 
379 	/*
380 	 * Verify the arguments.
381 	 */
382 	flags = uap->flags;
383 	if ((flags & SUN_MMAP_CANDO) == 0)
384 		return (EINVAL);
385 	if ((flags & SUN_MMAP_MASK) != MAP_SHARED &&
386 	    (flags & SUN_MMAP_MASK) != MAP_PRIVATE)
387 		return (EINVAL);
388 	flags &= ~SUN_MMAP_CANDO;
389 
390 	/*
391 	 * Special case: if fd refers to /dev/zero, map as MAP_ANON.  (XXX)
392 	 */
393 	fdp = p->p_fd;
394 	if ((unsigned)uap->fd < fdp->fd_nfiles &&			/*XXX*/
395 	    (fp = fdp->fd_ofiles[uap->fd]) != NULL &&			/*XXX*/
396 	    fp->f_type == DTYPE_VNODE &&				/*XXX*/
397 	    (vp = (struct vnode *)fp->f_data)->v_type == VCHR &&	/*XXX*/
398 	    vp->v_rdev == DEVZERO) {					/*XXX*/
399 		flags |= MAP_ANON;
400 		uap->fd = -1;
401 	}
402 
403 	/* All done, fix up fields and go. */
404 	uap->flags = flags;
405 	uap->qoff = (quad_t)uap->off;
406 	return (smmap(p, uap, retval));
407 }
408 
409 #define	MC_SYNC		1
410 #define	MC_LOCK		2
411 #define	MC_UNLOCK	3
412 #define	MC_ADVISE	4
413 #define	MC_LOCKAS	5
414 #define	MC_UNLOCKAS	6
415 
416 struct sun_mctl_args {
417 	caddr_t	addr;
418 	size_t	len;
419 	int	func;
420 	void	*arg;
421 };
422 sun_mctl(p, uap, retval)
423 	register struct proc *p;
424 	register struct sun_mctl_args *uap;
425 	int *retval;
426 {
427 
428 	switch (uap->func) {
429 
430 	case MC_ADVISE:		/* ignore for now */
431 		return (0);
432 
433 	case MC_SYNC:		/* translate to msync */
434 		return (msync(p, uap, retval));
435 
436 	default:
437 		return (EINVAL);
438 	}
439 }
440 
441 struct sun_setreuid_args {
442 	int	ruid;		/* not uid_t */
443 	int	euid;
444 };
445 sun_setreuid(p, uap, retval)
446 	struct proc *p;
447 	struct sun_setreuid_args *uap;
448 	int *retval;
449 {
450 	register struct pcred *pc = p->p_cred;
451 	register uid_t ruid, euid;
452 	int error;
453 
454 	if (uap->ruid == -1)
455 		ruid = pc->p_ruid;
456 	else
457 		ruid = uap->ruid;
458 	/*
459 	 * Allow setting real uid to previous effective, for swapping real and
460 	 * effective.  This should be:
461 	 *
462 	 * if (ruid != pc->p_ruid &&
463 	 *     (error = suser(pc->pc_ucred, &p->p_acflag)))
464 	 */
465 	if (ruid != pc->p_ruid && ruid != pc->pc_ucred->cr_uid /* XXX */ &&
466 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
467 		return (error);
468 	if (uap->euid == -1)
469 		euid = pc->pc_ucred->cr_uid;
470 	else
471 		euid = uap->euid;
472 	if (euid != pc->pc_ucred->cr_uid && euid != pc->p_ruid &&
473 	    euid != pc->p_svuid && (error = suser(pc->pc_ucred, &p->p_acflag)))
474 		return (error);
475 	/*
476 	 * Everything's okay, do it.  Copy credentials so other references do
477 	 * not see our changes.
478 	 */
479 	pc->pc_ucred = crcopy(pc->pc_ucred);
480 	pc->pc_ucred->cr_uid = euid;
481 	pc->p_ruid = ruid;
482 	p->p_flag |= SUGID;
483 	return (0);
484 }
485 
486 struct sun_setregid_args {
487 	int	rgid;		/* not gid_t */
488 	int	egid;
489 };
490 sun_setregid(p, uap, retval)
491 	struct proc *p;
492 	struct sun_setregid_args *uap;
493 	int *retval;
494 {
495 	register struct pcred *pc = p->p_cred;
496 	register gid_t rgid, egid;
497 	int error;
498 
499 	if (uap->rgid == -1)
500 		rgid = pc->p_rgid;
501 	else
502 		rgid = uap->rgid;
503 	/*
504 	 * Allow setting real gid to previous effective, for swapping real and
505 	 * effective.  This didn't really work correctly in 4.[23], but is
506 	 * preserved so old stuff doesn't fail.  This should be:
507 	 *
508 	 * if (rgid != pc->p_rgid &&
509 	 *     (error = suser(pc->pc_ucred, &p->p_acflag)))
510 	 */
511 	if (rgid != pc->p_rgid && rgid != pc->pc_ucred->cr_groups[0] /* XXX */ &&
512 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
513 		return (error);
514 	if (uap->egid == -1)
515 		egid = pc->pc_ucred->cr_groups[0];
516 	else
517 		egid = uap->egid;
518 	if (egid != pc->pc_ucred->cr_groups[0] && egid != pc->p_rgid &&
519 	    egid != pc->p_svgid && (error = suser(pc->pc_ucred, &p->p_acflag)))
520 		return (error);
521 	pc->pc_ucred = crcopy(pc->pc_ucred);
522 	pc->pc_ucred->cr_groups[0] = egid;
523 	pc->p_rgid = rgid;
524 	p->p_flag |= SUGID;
525 	return (0);
526 }
527 
528 struct sun_setsockopt_args {
529 	int	s;
530 	int	level;
531 	int	name;
532 	caddr_t	val;
533 	int	valsize;
534 };
535 sun_setsockopt(p, uap, retval)
536 	struct proc *p;
537 	register struct sun_setsockopt_args *uap;
538 	int *retval;
539 {
540 	struct file *fp;
541 	struct mbuf *m = NULL;
542 	int error;
543 
544 	if (error = getsock(p->p_fd, uap->s, &fp))
545 		return (error);
546 #define	SO_DONTLINGER (~SO_LINGER)
547 	if (uap->name == SO_DONTLINGER) {
548 		m = m_get(M_WAIT, MT_SOOPTS);
549 		if (m == NULL)
550 			return (ENOBUFS);
551 		mtod(m, struct linger *)->l_onoff = 0;
552 		m->m_len = sizeof(struct linger);
553 		return (sosetopt((struct socket *)fp->f_data, uap->level,
554 		    SO_LINGER, m));
555 	}
556 	if (uap->valsize > MLEN)
557 		return (EINVAL);
558 	if (uap->val) {
559 		m = m_get(M_WAIT, MT_SOOPTS);
560 		if (m == NULL)
561 			return (ENOBUFS);
562 		if (error = copyin(uap->val, mtod(m, caddr_t),
563 		    (u_int)uap->valsize)) {
564 			(void) m_free(m);
565 			return (error);
566 		}
567 		m->m_len = uap->valsize;
568 	}
569 	return (sosetopt((struct socket *)fp->f_data, uap->level,
570 	    uap->name, m));
571 }
572 
573 struct sun_fchroot_args {
574 	int	fdes;
575 };
576 sun_fchroot(p, uap, retval)
577 	register struct proc *p;
578 	register struct sun_fchroot_args *uap;
579 	int *retval;
580 {
581 	register struct filedesc *fdp = p->p_fd;
582 	register struct vnode *vp;
583 	struct file *fp;
584 	int error;
585 
586 	if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
587 		return (error);
588 	if ((error = getvnode(fdp, uap->fdes, &fp)) != 0)
589 		return (error);
590 	vp = (struct vnode *)fp->f_data;
591 	VOP_LOCK(vp);
592 	if (vp->v_type != VDIR)
593 		error = ENOTDIR;
594 	else
595 		error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p);
596 	VOP_UNLOCK(vp);
597 	if (error)
598 		return (error);
599 	VREF(vp);
600 	if (fdp->fd_rdir != NULL)
601 		vrele(fdp->fd_rdir);
602 	fdp->fd_rdir = vp;
603 	return (0);
604 }
605