xref: /csrg-svn/sys/kern/uipc_usrreq.c (revision 25899)
123443Smckusick /*
223443Smckusick  * Copyright (c) 1982 Regents of the University of California.
323443Smckusick  * All rights reserved.  The Berkeley software License Agreement
423443Smckusick  * specifies the terms and conditions for redistribution.
523443Smckusick  *
6*25899Skarels  *	@(#)uipc_usrreq.c	6.19 (Berkeley) 01/13/86
723443Smckusick  */
88925Sroot 
917105Sbloom #include "param.h"
1017105Sbloom #include "dir.h"
1117105Sbloom #include "user.h"
1217105Sbloom #include "mbuf.h"
1317105Sbloom #include "domain.h"
1417105Sbloom #include "protosw.h"
1517105Sbloom #include "socket.h"
1617105Sbloom #include "socketvar.h"
1717105Sbloom #include "unpcb.h"
1817105Sbloom #include "un.h"
1917105Sbloom #include "inode.h"
2017105Sbloom #include "file.h"
2117105Sbloom #include "stat.h"
228925Sroot 
238925Sroot /*
248925Sroot  * Unix communications domain.
2512760Ssam  *
2612760Ssam  * TODO:
2712760Ssam  *	SEQPACKET, RDM
2813119Ssam  *	rethink name space problems
2912760Ssam  *	need a proper out-of-band
308925Sroot  */
3113119Ssam struct	sockaddr sun_noname = { AF_UNIX };
3225632Skarels ino_t	unp_ino;			/* prototype for fake inode numbers */
338925Sroot 
348925Sroot /*ARGSUSED*/
3512760Ssam uipc_usrreq(so, req, m, nam, rights)
368925Sroot 	struct socket *so;
378925Sroot 	int req;
3812760Ssam 	struct mbuf *m, *nam, *rights;
398925Sroot {
408925Sroot 	struct unpcb *unp = sotounpcb(so);
418925Sroot 	register struct socket *so2;
428925Sroot 	int error = 0;
438925Sroot 
4425555Skarels 	if (req == PRU_CONTROL)
4525555Skarels 		return (EOPNOTSUPP);
4612760Ssam 	if (req != PRU_SEND && rights && rights->m_len) {
4712760Ssam 		error = EOPNOTSUPP;
4812760Ssam 		goto release;
4912760Ssam 	}
5012760Ssam 	if (unp == 0 && req != PRU_ATTACH) {
5112760Ssam 		error = EINVAL;
5212760Ssam 		goto release;
5312760Ssam 	}
548925Sroot 	switch (req) {
558925Sroot 
568925Sroot 	case PRU_ATTACH:
578925Sroot 		if (unp) {
589169Ssam 			error = EISCONN;
598925Sroot 			break;
608925Sroot 		}
619028Sroot 		error = unp_attach(so);
628925Sroot 		break;
638925Sroot 
648925Sroot 	case PRU_DETACH:
658925Sroot 		unp_detach(unp);
668925Sroot 		break;
678925Sroot 
689169Ssam 	case PRU_BIND:
699169Ssam 		error = unp_bind(unp, nam);
709169Ssam 		break;
719169Ssam 
729169Ssam 	case PRU_LISTEN:
739169Ssam 		if (unp->unp_inode == 0)
749169Ssam 			error = EINVAL;
759169Ssam 		break;
769169Ssam 
778925Sroot 	case PRU_CONNECT:
789028Sroot 		error = unp_connect(so, nam);
798925Sroot 		break;
808925Sroot 
8112760Ssam 	case PRU_CONNECT2:
8213115Ssam 		error = unp_connect2(so, (struct mbuf *)0,
8313115Ssam 		    (struct socket *)nam);
8412760Ssam 		break;
8512760Ssam 
868925Sroot 	case PRU_DISCONNECT:
878925Sroot 		unp_disconnect(unp);
888925Sroot 		break;
898925Sroot 
909169Ssam 	case PRU_ACCEPT:
91*25899Skarels 		/*
92*25899Skarels 		 * Pass back name of connected socket,
93*25899Skarels 		 * if it was bound and we are still connected
94*25899Skarels 		 * (our peer may have closed already!).
95*25899Skarels 		 */
96*25899Skarels 		if (unp->unp_conn && unp->unp_conn->unp_addr) {
9725632Skarels 			nam->m_len = unp->unp_conn->unp_addr->m_len;
9825632Skarels 			bcopy(mtod(unp->unp_conn->unp_addr, caddr_t),
9925632Skarels 			    mtod(nam, caddr_t), (unsigned)nam->m_len);
10025632Skarels 		} else {
10125632Skarels 			nam->m_len = sizeof(sun_noname);
10225632Skarels 			*(mtod(nam, struct sockaddr *)) = sun_noname;
10325632Skarels 		}
1048925Sroot 		break;
1058925Sroot 
1068925Sroot 	case PRU_SHUTDOWN:
1078925Sroot 		socantsendmore(so);
1088925Sroot 		unp_usrclosed(unp);
1098925Sroot 		break;
1108925Sroot 
1118925Sroot 	case PRU_RCVD:
1128925Sroot 		switch (so->so_type) {
1138925Sroot 
1148925Sroot 		case SOCK_DGRAM:
1158925Sroot 			panic("uipc 1");
11610139Ssam 			/*NOTREACHED*/
1178925Sroot 
11810139Ssam 		case SOCK_STREAM:
1198925Sroot #define	rcv (&so->so_rcv)
1208925Sroot #define snd (&so2->so_snd)
1218925Sroot 			if (unp->unp_conn == 0)
1228925Sroot 				break;
1238925Sroot 			so2 = unp->unp_conn->unp_socket;
1248925Sroot 			/*
12525632Skarels 			 * Adjust backpressure on sender
1268925Sroot 			 * and wakeup any waiting to write.
1278925Sroot 			 */
12825632Skarels 			snd->sb_mbmax += unp->unp_mbcnt - rcv->sb_mbcnt;
12925632Skarels 			unp->unp_mbcnt = rcv->sb_mbcnt;
13025632Skarels 			snd->sb_hiwat += unp->unp_cc - rcv->sb_cc;
13125632Skarels 			unp->unp_cc = rcv->sb_cc;
13217543Skarels 			sowwakeup(so2);
1338925Sroot #undef snd
1348925Sroot #undef rcv
1358925Sroot 			break;
1368925Sroot 
1378925Sroot 		default:
1388925Sroot 			panic("uipc 2");
1398925Sroot 		}
1408925Sroot 		break;
1418925Sroot 
1428925Sroot 	case PRU_SEND:
14325632Skarels 		if (rights) {
14425632Skarels 			error = unp_internalize(rights);
14525632Skarels 			if (error)
14625632Skarels 				break;
14725632Skarels 		}
1488925Sroot 		switch (so->so_type) {
1498925Sroot 
15025632Skarels 		case SOCK_DGRAM: {
15125632Skarels 			struct sockaddr *from;
15225632Skarels 
1539028Sroot 			if (nam) {
1548925Sroot 				if (unp->unp_conn) {
1558925Sroot 					error = EISCONN;
1568925Sroot 					break;
1578925Sroot 				}
1589028Sroot 				error = unp_connect(so, nam);
1598925Sroot 				if (error)
1608925Sroot 					break;
1618925Sroot 			} else {
1628925Sroot 				if (unp->unp_conn == 0) {
1638925Sroot 					error = ENOTCONN;
1648925Sroot 					break;
1658925Sroot 				}
1668925Sroot 			}
1678925Sroot 			so2 = unp->unp_conn->unp_socket;
16825632Skarels 			if (unp->unp_addr)
16925632Skarels 				from = mtod(unp->unp_addr, struct sockaddr *);
17025632Skarels 			else
17125632Skarels 				from = &sun_noname;
17225632Skarels 			if (sbspace(&so2->so_rcv) > 0 &&
17325632Skarels 			    sbappendaddr(&so2->so_rcv, from, m, rights)) {
17425632Skarels 				sorwakeup(so2);
17525632Skarels 				m = 0;
17625632Skarels 			} else
17725632Skarels 				error = ENOBUFS;
1789028Sroot 			if (nam)
1799169Ssam 				unp_disconnect(unp);
1808925Sroot 			break;
18125632Skarels 		}
1828925Sroot 
1838925Sroot 		case SOCK_STREAM:
1848925Sroot #define	rcv (&so2->so_rcv)
1858925Sroot #define	snd (&so->so_snd)
18623524Skarels 			if (so->so_state & SS_CANTSENDMORE) {
18723524Skarels 				error = EPIPE;
18823524Skarels 				break;
18923524Skarels 			}
1908925Sroot 			if (unp->unp_conn == 0)
1918925Sroot 				panic("uipc 3");
1928925Sroot 			so2 = unp->unp_conn->unp_socket;
1938925Sroot 			/*
19425632Skarels 			 * Send to paired receive port, and then reduce
19525632Skarels 			 * send buffer hiwater marks to maintain backpressure.
1968925Sroot 			 * Wake up readers.
1978925Sroot 			 */
19825632Skarels 			if (rights)
19925632Skarels 				sbappendrights(rcv, m, rights);
20025632Skarels 			else
20125632Skarels 				sbappend(rcv, m);
20225632Skarels 			snd->sb_mbmax -=
20325632Skarels 			    rcv->sb_mbcnt - unp->unp_conn->unp_mbcnt;
20425632Skarels 			unp->unp_conn->unp_mbcnt = rcv->sb_mbcnt;
20525632Skarels 			snd->sb_hiwat -= rcv->sb_cc - unp->unp_conn->unp_cc;
20625632Skarels 			unp->unp_conn->unp_cc = rcv->sb_cc;
20717543Skarels 			sorwakeup(so2);
20817543Skarels 			m = 0;
2098925Sroot #undef snd
2108925Sroot #undef rcv
2118925Sroot 			break;
2128925Sroot 
2138925Sroot 		default:
2148925Sroot 			panic("uipc 4");
2158925Sroot 		}
2168925Sroot 		break;
2178925Sroot 
2188925Sroot 	case PRU_ABORT:
2198925Sroot 		unp_drop(unp, ECONNABORTED);
2208925Sroot 		break;
2218925Sroot 
2228925Sroot 	case PRU_SENSE:
22316973Skarels 		((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
22416973Skarels 		if (so->so_type == SOCK_STREAM && unp->unp_conn != 0) {
22516973Skarels 			so2 = unp->unp_conn->unp_socket;
22616973Skarels 			((struct stat *) m)->st_blksize += so2->so_rcv.sb_cc;
22716973Skarels 		}
22821110Skarels 		((struct stat *) m)->st_dev = NODEV;
22925632Skarels 		if (unp->unp_ino == 0)
23025632Skarels 			unp->unp_ino = unp_ino++;
23125632Skarels 		((struct stat *) m)->st_ino = unp->unp_ino;
23216973Skarels 		return (0);
2338925Sroot 
2348925Sroot 	case PRU_RCVOOB:
23516774Sbloom 		return (EOPNOTSUPP);
2368925Sroot 
2378925Sroot 	case PRU_SENDOOB:
23817543Skarels 		error = EOPNOTSUPP;
2398925Sroot 		break;
2408925Sroot 
2418925Sroot 	case PRU_SOCKADDR:
2428925Sroot 		break;
2438925Sroot 
24414121Ssam 	case PRU_PEERADDR:
24514121Ssam 		break;
24614121Ssam 
2478925Sroot 	case PRU_SLOWTIMO:
2488925Sroot 		break;
2498925Sroot 
2508925Sroot 	default:
2518925Sroot 		panic("piusrreq");
2528925Sroot 	}
25312760Ssam release:
25412760Ssam 	if (m)
25512760Ssam 		m_freem(m);
25611709Ssam 	return (error);
2578925Sroot }
2588925Sroot 
25916973Skarels /*
26025632Skarels  * Both send and receive buffers are allocated PIPSIZ bytes of buffering
26125632Skarels  * for stream sockets, although the total for sender and receiver is
26225632Skarels  * actually only PIPSIZ.
26316973Skarels  * Datagram sockets really use the sendspace as the maximum datagram size,
26416973Skarels  * and don't really want to reserve the sendspace.  Their recvspace should
26516973Skarels  * be large enough for at least one max-size datagram plus address.
26616973Skarels  */
26716973Skarels #define	PIPSIZ	4096
26816973Skarels int	unpst_sendspace = PIPSIZ;
26925632Skarels int	unpst_recvspace = PIPSIZ;
27016973Skarels int	unpdg_sendspace = 2*1024;	/* really max datagram size */
27116973Skarels int	unpdg_recvspace = 4*1024;
2728925Sroot 
27325632Skarels int	unp_rights;			/* file descriptors in flight */
27425632Skarels 
2759169Ssam unp_attach(so)
2768925Sroot 	struct socket *so;
2778925Sroot {
2789169Ssam 	register struct mbuf *m;
2798925Sroot 	register struct unpcb *unp;
2808925Sroot 	int error;
2818925Sroot 
28216973Skarels 	switch (so->so_type) {
28316973Skarels 
28416973Skarels 	case SOCK_STREAM:
28516973Skarels 		error = soreserve(so, unpst_sendspace, unpst_recvspace);
28616973Skarels 		break;
28716973Skarels 
28816973Skarels 	case SOCK_DGRAM:
28916973Skarels 		error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
29016973Skarels 		break;
29116973Skarels 	}
2928925Sroot 	if (error)
29310139Ssam 		return (error);
2949637Ssam 	m = m_getclr(M_DONTWAIT, MT_PCB);
29510139Ssam 	if (m == NULL)
29610139Ssam 		return (ENOBUFS);
2978925Sroot 	unp = mtod(m, struct unpcb *);
2988925Sroot 	so->so_pcb = (caddr_t)unp;
2998925Sroot 	unp->unp_socket = so;
3008925Sroot 	return (0);
3018925Sroot }
3028925Sroot 
3038925Sroot unp_detach(unp)
3049169Ssam 	register struct unpcb *unp;
3058925Sroot {
3068925Sroot 
3078925Sroot 	if (unp->unp_inode) {
30817020Skarels 		unp->unp_inode->i_socket = 0;
3098925Sroot 		irele(unp->unp_inode);
3108925Sroot 		unp->unp_inode = 0;
3118925Sroot 	}
3128925Sroot 	if (unp->unp_conn)
3138925Sroot 		unp_disconnect(unp);
3148925Sroot 	while (unp->unp_refs)
3158925Sroot 		unp_drop(unp->unp_refs, ECONNRESET);
3168925Sroot 	soisdisconnected(unp->unp_socket);
3178925Sroot 	unp->unp_socket->so_pcb = 0;
31825632Skarels 	m_freem(unp->unp_addr);
3199169Ssam 	(void) m_free(dtom(unp));
32025632Skarels 	if (unp_rights)
32125632Skarels 		unp_gc();
3228925Sroot }
3238925Sroot 
3249169Ssam unp_bind(unp, nam)
3258925Sroot 	struct unpcb *unp;
3269169Ssam 	struct mbuf *nam;
3278925Sroot {
3289169Ssam 	struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
3298925Sroot 	register struct inode *ip;
33016695Smckusick 	register struct nameidata *ndp = &u.u_nd;
3318925Sroot 	int error;
3328925Sroot 
33316695Smckusick 	ndp->ni_dirp = soun->sun_path;
33425232Smckusick 	if (unp->unp_inode != NULL || nam->m_len == MLEN)
33512760Ssam 		return (EINVAL);
33612760Ssam 	*(mtod(nam, caddr_t) + nam->m_len) = 0;
33712760Ssam /* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
33816695Smckusick 	ndp->ni_nameiop = CREATE | FOLLOW;
33916695Smckusick 	ndp->ni_segflg = UIO_SYSSPACE;
34016695Smckusick 	ip = namei(ndp);
3418925Sroot 	if (ip) {
3428925Sroot 		iput(ip);
34310139Ssam 		return (EADDRINUSE);
3448925Sroot 	}
34511828Ssam 	if (error = u.u_error) {
34611828Ssam 		u.u_error = 0;			/* XXX */
34711828Ssam 		return (error);
34811828Ssam 	}
34916695Smckusick 	ip = maknode(IFSOCK | 0777, ndp);
3508925Sroot 	if (ip == NULL) {
3518925Sroot 		error = u.u_error;		/* XXX */
3528925Sroot 		u.u_error = 0;			/* XXX */
3538925Sroot 		return (error);
3548925Sroot 	}
3558925Sroot 	ip->i_socket = unp->unp_socket;
3568925Sroot 	unp->unp_inode = ip;
35725632Skarels 	unp->unp_addr = m_copy(nam, 0, (int)M_COPYALL);
3588925Sroot 	iunlock(ip);			/* but keep reference */
3598925Sroot 	return (0);
3608925Sroot }
3618925Sroot 
3629169Ssam unp_connect(so, nam)
3638925Sroot 	struct socket *so;
3649169Ssam 	struct mbuf *nam;
3658925Sroot {
3669169Ssam 	register struct sockaddr_un *soun = mtod(nam, struct sockaddr_un *);
3679169Ssam 	register struct inode *ip;
3688925Sroot 	int error;
36912760Ssam 	register struct socket *so2;
37016695Smckusick 	register struct nameidata *ndp = &u.u_nd;
3718925Sroot 
37216695Smckusick 	ndp->ni_dirp = soun->sun_path;
37312760Ssam 	if (nam->m_len + (nam->m_off - MMINOFF) == MLEN)
37412760Ssam 		return (EMSGSIZE);
37512760Ssam 	*(mtod(nam, caddr_t) + nam->m_len) = 0;
37616695Smckusick 	ndp->ni_nameiop = LOOKUP | FOLLOW;
37716695Smckusick 	ndp->ni_segflg = UIO_SYSSPACE;
37816695Smckusick 	ip = namei(ndp);
3798925Sroot 	if (ip == 0) {
3808925Sroot 		error = u.u_error;
3818925Sroot 		u.u_error = 0;
38210139Ssam 		return (error);		/* XXX */
3838925Sroot 	}
38417543Skarels 	if (access(ip, IWRITE)) {
38517543Skarels 		error = u.u_error;
38617543Skarels 		u.u_error = 0; 		/* XXX */
38717543Skarels 		goto bad;
38817543Skarels 	}
3898925Sroot 	if ((ip->i_mode&IFMT) != IFSOCK) {
3908925Sroot 		error = ENOTSOCK;
3918925Sroot 		goto bad;
3928925Sroot 	}
3938925Sroot 	so2 = ip->i_socket;
3948925Sroot 	if (so2 == 0) {
3958925Sroot 		error = ECONNREFUSED;
3968925Sroot 		goto bad;
3978925Sroot 	}
39813115Ssam 	if (so->so_type != so2->so_type) {
39913115Ssam 		error = EPROTOTYPE;
40013115Ssam 		goto bad;
40113115Ssam 	}
40213115Ssam 	if (so->so_proto->pr_flags & PR_CONNREQUIRED &&
40313115Ssam 	    ((so2->so_options&SO_ACCEPTCONN) == 0 ||
40413115Ssam 	     (so2 = sonewconn(so2)) == 0)) {
40513115Ssam 		error = ECONNREFUSED;
40613115Ssam 		goto bad;
40713115Ssam 	}
40812760Ssam 	error = unp_connect2(so, nam, so2);
40912760Ssam bad:
41012760Ssam 	iput(ip);
41112760Ssam 	return (error);
41212760Ssam }
41312760Ssam 
41412760Ssam unp_connect2(so, sonam, so2)
41512760Ssam 	register struct socket *so;
41612760Ssam 	struct mbuf *sonam;
41712760Ssam 	register struct socket *so2;
41812760Ssam {
41912760Ssam 	register struct unpcb *unp = sotounpcb(so);
42012760Ssam 	register struct unpcb *unp2;
42112760Ssam 
42212760Ssam 	if (so2->so_type != so->so_type)
42312760Ssam 		return (EPROTOTYPE);
42414049Ssam 	unp2 = sotounpcb(so2);
42514049Ssam 	unp->unp_conn = unp2;
4268925Sroot 	switch (so->so_type) {
4278925Sroot 
4288925Sroot 	case SOCK_DGRAM:
4298925Sroot 		unp->unp_nextref = unp2->unp_refs;
4308925Sroot 		unp2->unp_refs = unp;
43117543Skarels 		soisconnected(so);
4328925Sroot 		break;
4338925Sroot 
4348925Sroot 	case SOCK_STREAM:
4359169Ssam 		unp2->unp_conn = unp;
43614049Ssam 		soisconnected(so2);
43714049Ssam 		soisconnected(so);
4388925Sroot 		break;
4398925Sroot 
4408925Sroot 	default:
44112760Ssam 		panic("unp_connect2");
4428925Sroot 	}
4438925Sroot 	return (0);
4448925Sroot }
4459169Ssam 
4469169Ssam unp_disconnect(unp)
4479169Ssam 	struct unpcb *unp;
4489169Ssam {
4499169Ssam 	register struct unpcb *unp2 = unp->unp_conn;
4509169Ssam 
4519169Ssam 	if (unp2 == 0)
4529169Ssam 		return;
4539169Ssam 	unp->unp_conn = 0;
4549169Ssam 	switch (unp->unp_socket->so_type) {
4559169Ssam 
4569169Ssam 	case SOCK_DGRAM:
4579169Ssam 		if (unp2->unp_refs == unp)
4589169Ssam 			unp2->unp_refs = unp->unp_nextref;
4599169Ssam 		else {
4609169Ssam 			unp2 = unp2->unp_refs;
4619169Ssam 			for (;;) {
4629169Ssam 				if (unp2 == 0)
4639169Ssam 					panic("unp_disconnect");
4649169Ssam 				if (unp2->unp_nextref == unp)
4659169Ssam 					break;
4669169Ssam 				unp2 = unp2->unp_nextref;
4679169Ssam 			}
4689169Ssam 			unp2->unp_nextref = unp->unp_nextref;
4699169Ssam 		}
4709169Ssam 		unp->unp_nextref = 0;
47121768Skarels 		unp->unp_socket->so_state &= ~SS_ISCONNECTED;
4729169Ssam 		break;
4739169Ssam 
4749169Ssam 	case SOCK_STREAM:
47514049Ssam 		soisdisconnected(unp->unp_socket);
4769169Ssam 		unp2->unp_conn = 0;
4779169Ssam 		soisdisconnected(unp2->unp_socket);
4789169Ssam 		break;
4799169Ssam 	}
4809169Ssam }
4819169Ssam 
48212760Ssam #ifdef notdef
4839169Ssam unp_abort(unp)
4849169Ssam 	struct unpcb *unp;
4859169Ssam {
4869169Ssam 
4879169Ssam 	unp_detach(unp);
4889169Ssam }
48912760Ssam #endif
4909169Ssam 
4919169Ssam /*ARGSUSED*/
4929169Ssam unp_usrclosed(unp)
4939169Ssam 	struct unpcb *unp;
4949169Ssam {
4959169Ssam 
4969169Ssam }
4979169Ssam 
4989169Ssam unp_drop(unp, errno)
4999169Ssam 	struct unpcb *unp;
5009169Ssam 	int errno;
5019169Ssam {
50216054Skarels 	struct socket *so = unp->unp_socket;
5039169Ssam 
50416054Skarels 	so->so_error = errno;
5059169Ssam 	unp_disconnect(unp);
50616054Skarels 	if (so->so_head) {
50716054Skarels 		so->so_pcb = (caddr_t) 0;
50825632Skarels 		m_freem(unp->unp_addr);
50916054Skarels 		(void) m_free(dtom(unp));
51016054Skarels 		sofree(so);
51116054Skarels 	}
5129169Ssam }
5139169Ssam 
51412760Ssam #ifdef notdef
5159169Ssam unp_drain()
5169169Ssam {
5179169Ssam 
5189169Ssam }
51912760Ssam #endif
52012760Ssam 
52112760Ssam unp_externalize(rights)
52212760Ssam 	struct mbuf *rights;
52312760Ssam {
52412760Ssam 	int newfds = rights->m_len / sizeof (int);
52512760Ssam 	register int i;
52612760Ssam 	register struct file **rp = mtod(rights, struct file **);
52712760Ssam 	register struct file *fp;
52812760Ssam 	int f;
52912760Ssam 
53012760Ssam 	if (newfds > ufavail()) {
53112760Ssam 		for (i = 0; i < newfds; i++) {
53212760Ssam 			fp = *rp;
53312760Ssam 			unp_discard(fp);
53412760Ssam 			*rp++ = 0;
53512760Ssam 		}
53612760Ssam 		return (EMSGSIZE);
53712760Ssam 	}
53812760Ssam 	for (i = 0; i < newfds; i++) {
53912760Ssam 		f = ufalloc(0);
54012760Ssam 		if (f < 0)
54112760Ssam 			panic("unp_externalize");
54212760Ssam 		fp = *rp;
54312760Ssam 		u.u_ofile[f] = fp;
54412760Ssam 		fp->f_msgcount--;
54525632Skarels 		unp_rights--;
54614927Smckusick 		*(int *)rp++ = f;
54712760Ssam 	}
54812760Ssam 	return (0);
54912760Ssam }
55012760Ssam 
55112760Ssam unp_internalize(rights)
55212760Ssam 	struct mbuf *rights;
55312760Ssam {
55412760Ssam 	register struct file **rp;
55512760Ssam 	int oldfds = rights->m_len / sizeof (int);
55612760Ssam 	register int i;
55712760Ssam 	register struct file *fp;
55812760Ssam 
55912760Ssam 	rp = mtod(rights, struct file **);
56013084Ssam 	for (i = 0; i < oldfds; i++)
56112760Ssam 		if (getf(*(int *)rp++) == 0)
56212760Ssam 			return (EBADF);
56312760Ssam 	rp = mtod(rights, struct file **);
56413084Ssam 	for (i = 0; i < oldfds; i++) {
56512760Ssam 		fp = getf(*(int *)rp);
56612760Ssam 		*rp++ = fp;
56712760Ssam 		fp->f_count++;
56812760Ssam 		fp->f_msgcount++;
56925632Skarels 		unp_rights++;
57012760Ssam 	}
57112760Ssam 	return (0);
57212760Ssam }
57312760Ssam 
57412760Ssam int	unp_defer, unp_gcing;
57512760Ssam int	unp_mark();
57616995Skarels extern	struct domain unixdomain;
57712760Ssam 
57812760Ssam unp_gc()
57912760Ssam {
58012760Ssam 	register struct file *fp;
58112760Ssam 	register struct socket *so;
58212760Ssam 
58312760Ssam 	if (unp_gcing)
58412760Ssam 		return;
58512760Ssam 	unp_gcing = 1;
58612760Ssam restart:
58712760Ssam 	unp_defer = 0;
58812760Ssam 	for (fp = file; fp < fileNFILE; fp++)
58912760Ssam 		fp->f_flag &= ~(FMARK|FDEFER);
59012760Ssam 	do {
59112760Ssam 		for (fp = file; fp < fileNFILE; fp++) {
59212760Ssam 			if (fp->f_count == 0)
59312760Ssam 				continue;
59412760Ssam 			if (fp->f_flag & FDEFER) {
59512760Ssam 				fp->f_flag &= ~FDEFER;
59612760Ssam 				unp_defer--;
59712760Ssam 			} else {
59812760Ssam 				if (fp->f_flag & FMARK)
59912760Ssam 					continue;
60012760Ssam 				if (fp->f_count == fp->f_msgcount)
60112760Ssam 					continue;
60212760Ssam 				fp->f_flag |= FMARK;
60312760Ssam 			}
60412760Ssam 			if (fp->f_type != DTYPE_SOCKET)
60512760Ssam 				continue;
60612760Ssam 			so = (struct socket *)fp->f_data;
60716995Skarels 			if (so->so_proto->pr_domain != &unixdomain ||
60821768Skarels 			    (so->so_proto->pr_flags&PR_RIGHTS) == 0)
60912760Ssam 				continue;
61012760Ssam 			if (so->so_rcv.sb_flags & SB_LOCK) {
61112760Ssam 				sbwait(&so->so_rcv);
61212760Ssam 				goto restart;
61312760Ssam 			}
61412760Ssam 			unp_scan(so->so_rcv.sb_mb, unp_mark);
61512760Ssam 		}
61612760Ssam 	} while (unp_defer);
61712760Ssam 	for (fp = file; fp < fileNFILE; fp++) {
61812760Ssam 		if (fp->f_count == 0)
61912760Ssam 			continue;
62025632Skarels 		if (fp->f_count == fp->f_msgcount && (fp->f_flag & FMARK) == 0)
62125632Skarels 			while (fp->f_msgcount)
62225632Skarels 				unp_discard(fp);
62312760Ssam 	}
62412760Ssam 	unp_gcing = 0;
62512760Ssam }
62612760Ssam 
62716995Skarels unp_dispose(m)
62816995Skarels 	struct mbuf *m;
62916995Skarels {
63016995Skarels 	int unp_discard();
63116995Skarels 
63217020Skarels 	if (m)
63317020Skarels 		unp_scan(m, unp_discard);
63416995Skarels }
63516995Skarels 
63616995Skarels unp_scan(m0, op)
63716995Skarels 	register struct mbuf *m0;
63812760Ssam 	int (*op)();
63912760Ssam {
64016995Skarels 	register struct mbuf *m;
64112760Ssam 	register struct file **rp;
64212760Ssam 	register int i;
64317020Skarels 	int qfds;
64412760Ssam 
64516995Skarels 	while (m0) {
64616995Skarels 		for (m = m0; m; m = m->m_next)
64716995Skarels 			if (m->m_type == MT_RIGHTS && m->m_len) {
64816995Skarels 				qfds = m->m_len / sizeof (struct file *);
64916995Skarels 				rp = mtod(m, struct file **);
65016995Skarels 				for (i = 0; i < qfds; i++)
65116995Skarels 					(*op)(*rp++);
65216995Skarels 				break;		/* XXX, but saves time */
65316995Skarels 			}
65417020Skarels 		m0 = m0->m_act;
65512760Ssam 	}
65612760Ssam }
65712760Ssam 
65812760Ssam unp_mark(fp)
65912760Ssam 	struct file *fp;
66012760Ssam {
66112760Ssam 
66212760Ssam 	if (fp->f_flag & FMARK)
66312760Ssam 		return;
66412760Ssam 	unp_defer++;
66512760Ssam 	fp->f_flag |= (FMARK|FDEFER);
66612760Ssam }
66712760Ssam 
66812760Ssam unp_discard(fp)
66912760Ssam 	struct file *fp;
67012760Ssam {
67112760Ssam 
67212760Ssam 	fp->f_msgcount--;
67325632Skarels 	unp_rights--;
67413084Ssam 	closef(fp);
67512760Ssam }
676