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