xref: /csrg-svn/sys/nfs/nfs_socket.c (revision 39754)
138414Smckusick /*
238414Smckusick  * Copyright (c) 1989 The Regents of the University of California.
338414Smckusick  * All rights reserved.
438414Smckusick  *
538414Smckusick  * This code is derived from software contributed to Berkeley by
638414Smckusick  * Rick Macklem at The University of Guelph.
738414Smckusick  *
838414Smckusick  * Redistribution and use in source and binary forms are permitted
938414Smckusick  * provided that the above copyright notice and this paragraph are
1038414Smckusick  * duplicated in all such forms and that any documentation,
1138414Smckusick  * advertising materials, and other materials related to such
1238414Smckusick  * distribution and use acknowledge that the software was developed
1338414Smckusick  * by the University of California, Berkeley.  The name of the
1438414Smckusick  * University may not be used to endorse or promote products derived
1538414Smckusick  * from this software without specific prior written permission.
1638414Smckusick  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1738414Smckusick  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1838414Smckusick  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1938414Smckusick  *
20*39754Smckusick  *	@(#)nfs_socket.c	7.5 (Berkeley) 12/20/89
2138414Smckusick  */
2238414Smckusick 
2338414Smckusick /*
2438414Smckusick  * Socket operations for use by nfs (similar to uipc_socket.c, but never
2538414Smckusick  * with copies to/from a uio vector)
2638414Smckusick  * NB: For now, they only work for UDP datagram sockets.
2738414Smckusick  * (Use on stream sockets would require some record boundary mark in the
28*39754Smckusick  *  stream as defined by "RPC: Remote Procedure Call Protocol
29*39754Smckusick  *  Specification" RFC1057 Section 10)
3038414Smckusick  *  and different versions of send, receive and reply that do not assume
3138414Smckusick  *  an atomic protocol
3238414Smckusick  */
3338414Smckusick 
3438414Smckusick #include "types.h"
3538414Smckusick #include "param.h"
3638414Smckusick #include "uio.h"
3738414Smckusick #include "user.h"
3838414Smckusick #include "mount.h"
3938414Smckusick #include "kernel.h"
4038414Smckusick #include "malloc.h"
4138414Smckusick #include "mbuf.h"
4238414Smckusick #include "vnode.h"
4338414Smckusick #include "domain.h"
4438414Smckusick #include "protosw.h"
4538414Smckusick #include "socket.h"
4638414Smckusick #include "socketvar.h"
4738414Smckusick #include "netinet/in.h"
4838414Smckusick #include "rpcv2.h"
4938414Smckusick #include "nfsv2.h"
5038414Smckusick #include "nfs.h"
5138414Smckusick #include "xdr_subs.h"
5238414Smckusick #include "nfsm_subs.h"
5338414Smckusick #include "nfsmount.h"
5438414Smckusick 
5538414Smckusick #define	TRUE	1
5638414Smckusick 
5738414Smckusick /* set lock on sockbuf sb, sleep at neg prio */
5838414Smckusick #define nfs_sblock(sb) { \
5938414Smckusick 	while ((sb)->sb_flags & SB_LOCK) { \
6038414Smckusick 		(sb)->sb_flags |= SB_WANT; \
6138414Smckusick 		sleep((caddr_t)&(sb)->sb_flags, PZERO-1); \
6238414Smckusick 	} \
6338414Smckusick 	(sb)->sb_flags |= SB_LOCK; \
6438414Smckusick }
6538414Smckusick 
6638414Smckusick /*
6738414Smckusick  * External data, mostly RPC constants in XDR form
6838414Smckusick  */
6938414Smckusick extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
7038414Smckusick 	rpc_msgaccepted, rpc_call;
7138414Smckusick extern u_long nfs_prog, nfs_vers;
7238414Smckusick int	nfsrv_null(),
7338414Smckusick 	nfsrv_getattr(),
7438414Smckusick 	nfsrv_setattr(),
7538414Smckusick 	nfsrv_lookup(),
7638414Smckusick 	nfsrv_readlink(),
7738414Smckusick 	nfsrv_read(),
7838414Smckusick 	nfsrv_write(),
7938414Smckusick 	nfsrv_create(),
8038414Smckusick 	nfsrv_remove(),
8138414Smckusick 	nfsrv_rename(),
8238414Smckusick 	nfsrv_link(),
8338414Smckusick 	nfsrv_symlink(),
8438414Smckusick 	nfsrv_mkdir(),
8538414Smckusick 	nfsrv_rmdir(),
8638414Smckusick 	nfsrv_readdir(),
8738414Smckusick 	nfsrv_statfs(),
8838414Smckusick 	nfsrv_noop();
8938414Smckusick 
9038414Smckusick int (*nfsrv_procs[NFS_NPROCS])() = {
9138414Smckusick 	nfsrv_null,
9238414Smckusick 	nfsrv_getattr,
9338414Smckusick 	nfsrv_setattr,
9438414Smckusick 	nfsrv_noop,
9538414Smckusick 	nfsrv_lookup,
9638414Smckusick 	nfsrv_readlink,
9738414Smckusick 	nfsrv_read,
9838414Smckusick 	nfsrv_noop,
9938414Smckusick 	nfsrv_write,
10038414Smckusick 	nfsrv_create,
10138414Smckusick 	nfsrv_remove,
10238414Smckusick 	nfsrv_rename,
10338414Smckusick 	nfsrv_link,
10438414Smckusick 	nfsrv_symlink,
10538414Smckusick 	nfsrv_mkdir,
10638414Smckusick 	nfsrv_rmdir,
10738414Smckusick 	nfsrv_readdir,
10838414Smckusick 	nfsrv_statfs,
10938414Smckusick };
11038414Smckusick 
11138414Smckusick 
11238414Smckusick /*
11338414Smckusick  * This is a stripped down version of sosend() specific to
11438414Smckusick  * udp/ip and uses the mbuf list provdied
11538414Smckusick  */
11638414Smckusick nfs_udpsend(so, nam, top, flags, siz)
11738414Smckusick 	register struct socket *so;
11838414Smckusick 	struct mbuf *nam;
11938414Smckusick 	struct mbuf *top;
12038414Smckusick 	int flags;
12138414Smckusick 	int siz;
12238414Smckusick {
12338414Smckusick 	register int space;
12439494Smckusick 	int error = 0, s, dontroute;
12538414Smckusick 
12638414Smckusick 	dontroute =
12738414Smckusick 	    (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
12838414Smckusick 	    (so->so_proto->pr_flags & PR_ATOMIC);
12938414Smckusick #define	snderr(errno)	{ error = errno; splx(s); goto release; }
13038414Smckusick 
13138414Smckusick #ifdef MGETHDR
13238414Smckusick 	top->m_pkthdr.len = siz;
13338414Smckusick #endif
13438414Smckusick restart:
13538414Smckusick 	nfs_sblock(&so->so_snd);
13638414Smckusick 	s = splnet();
13738414Smckusick 	if (so->so_state & SS_CANTSENDMORE)
13838414Smckusick 		snderr(EPIPE);
13938414Smckusick 	if (so->so_error)
14038414Smckusick 		snderr(so->so_error);
14138414Smckusick 	space = sbspace(&so->so_snd);
14238414Smckusick 	if (space < siz) {
14338414Smckusick 		sbunlock(&so->so_snd);
14438414Smckusick 		nfs_sbwait(&so->so_snd);
14538414Smckusick 		splx(s);
14638414Smckusick 		goto restart;
14738414Smckusick 	}
14838414Smckusick 	splx(s);
14938414Smckusick 	if (dontroute)
15038414Smckusick 		so->so_options |= SO_DONTROUTE;
15138414Smckusick 	s = splnet();					/* XXX */
15238414Smckusick 	error = (*so->so_proto->pr_usrreq)(so,
15338414Smckusick 	    PRU_SEND,
15438414Smckusick 	    top, (caddr_t)nam, (struct mbuf *)0, (struct mbuf *)0);
15538414Smckusick 	splx(s);
15638414Smckusick 	if (dontroute)
15738414Smckusick 		so->so_options &= ~SO_DONTROUTE;
15838414Smckusick 	top = (struct mbuf *)0;
15938414Smckusick 
16038414Smckusick release:
16138414Smckusick 	sbunlock(&so->so_snd);
16238414Smckusick 	if (top)
16338414Smckusick 		m_freem(top);
16438414Smckusick 	return (error);
16538414Smckusick }
16638414Smckusick 
16738414Smckusick /*
16838414Smckusick  * This is a stripped down udp specific version of soreceive()
16938414Smckusick  */
170*39754Smckusick nfs_udpreceive(so, msk, mtch, aname, mp)
17138414Smckusick 	register struct socket *so;
172*39754Smckusick 	u_long msk;
173*39754Smckusick 	u_long mtch;
17438414Smckusick 	struct mbuf **aname;
17538414Smckusick 	struct mbuf **mp;
17638414Smckusick {
17738414Smckusick 	register struct mbuf *m;
17838414Smckusick 	int s, error = 0;
17938414Smckusick 	struct mbuf *nextrecord;
180*39754Smckusick 	struct sockaddr_in *saddr;
18138414Smckusick 
18238414Smckusick 	if (aname)
18338414Smckusick 		*aname = 0;
18438414Smckusick 
18538414Smckusick restart:
18638414Smckusick 	sblock(&so->so_rcv);
18738414Smckusick 	s = splnet();
18838414Smckusick 
18938414Smckusick 	if (so->so_rcv.sb_cc == 0) {
19038414Smckusick 		if (so->so_error) {
19138414Smckusick 			error = so->so_error;
19238414Smckusick 			so->so_error = 0;
19338414Smckusick 			goto release;
19438414Smckusick 		}
19538414Smckusick 		if (so->so_state & SS_CANTRCVMORE)
19638414Smckusick 			goto release;
19738414Smckusick 		sbunlock(&so->so_rcv);
19838414Smckusick 		sbwait(&so->so_rcv);
19938414Smckusick 		splx(s);
20038414Smckusick 		goto restart;
20138414Smckusick 	}
20238414Smckusick 	m = so->so_rcv.sb_mb;
20338414Smckusick 	if (m == 0)
20438414Smckusick 		panic("nfs_receive 1");
20538414Smckusick 	nextrecord = m->m_nextpkt;
20638414Smckusick 	if (m->m_type != MT_SONAME)
20738414Smckusick 		panic("nfs_receive 1a");
208*39754Smckusick 	if (msk) {
209*39754Smckusick 		saddr = mtod(m, struct sockaddr_in *);
210*39754Smckusick 		if ((saddr->sin_addr.s_addr & msk) != mtch) {
211*39754Smckusick 			sbdroprecord(&so->so_rcv);
212*39754Smckusick 			sbunlock(&so->so_rcv);
213*39754Smckusick 			splx(s);
214*39754Smckusick 			goto restart;
215*39754Smckusick 		}
216*39754Smckusick 	}
21738414Smckusick 	sbfree(&so->so_rcv, m);
21838414Smckusick 	if (aname) {
21938414Smckusick 		*aname = m;
22038414Smckusick 		so->so_rcv.sb_mb = m->m_next;
22138414Smckusick 		m->m_next = 0;
22238414Smckusick 		m = so->so_rcv.sb_mb;
22338414Smckusick 	} else {
22438414Smckusick 		MFREE(m, so->so_rcv.sb_mb);
22538414Smckusick 		m = so->so_rcv.sb_mb;
22638414Smckusick 	}
22738414Smckusick 	if (m && m->m_type == MT_RIGHTS)
22838414Smckusick 		panic("nfs_receive 2");
22938414Smckusick 	if (m && m->m_type == MT_CONTROL) {
23038414Smckusick 		sbfree(&so->so_rcv, m);
23138414Smckusick 		MFREE(m, so->so_rcv.sb_mb);
23238414Smckusick 		m = so->so_rcv.sb_mb;
23338414Smckusick 	}
23438414Smckusick 	*mp = m;
23538414Smckusick 	while (m) {
23638414Smckusick 		if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
23738414Smckusick 			panic("nfs_receive 3");
23838414Smckusick 		sbfree(&so->so_rcv, m);
23938414Smckusick 		m = so->so_rcv.sb_mb = m->m_next;
24038414Smckusick 	}
24138414Smckusick 	so->so_rcv.sb_mb = nextrecord;
24238414Smckusick 	so->so_state &= ~SS_RCVATMARK;	/* Necessary ?? */
24338414Smckusick release:
24438414Smckusick 	sbunlock(&so->so_rcv);
24538414Smckusick 	splx(s);
24638414Smckusick 	return (error);
24738414Smckusick }
24838414Smckusick 
24938414Smckusick struct nfsreq nfsreqh = {
25038414Smckusick 	(struct nfsreq *)0,
25138414Smckusick 	(struct nfsreq *)0,
25238414Smckusick 	(struct mbuf *)0,
25338414Smckusick 	(struct mbuf *)0,
25438414Smckusick 	(struct nfsmount *)0,
25538414Smckusick 	0, 0, 0, 0, 0,
25638414Smckusick };
25738414Smckusick 
25838414Smckusick struct rpc_replyhead {
25938414Smckusick 	u_long	r_xid;
26038414Smckusick 	u_long	r_rep;
26138414Smckusick };
26238414Smckusick 
26338414Smckusick /*
26438414Smckusick  * Implement receipt of reply on a socket.
26538414Smckusick  * We depend on the way that records are added to the sockbuf
26638414Smckusick  * by sbappend*.  In particular, each record (mbufs linked through m_next)
26738414Smckusick  * must begin with an address, followed by optional MT_CONTROL mbuf
26838414Smckusick  * and then zero or more mbufs of data.
26938414Smckusick  * Although the sockbuf is locked, new data may still be appended,
27038414Smckusick  * and thus we must maintain consistency of the sockbuf during that time.
27138414Smckusick  * We must search through the list of received datagrams matching them
27238414Smckusick  * with outstanding requests using the xid, until ours is found.
27338414Smckusick  */
27439494Smckusick /* ARGSUSED */
27539344Smckusick nfs_udpreply(so, mntp, myrep)
27638414Smckusick 	register struct socket *so;
27738414Smckusick 	struct nfsmount *mntp;
27839344Smckusick 	struct nfsreq *myrep;
27938414Smckusick {
28038414Smckusick 	register struct mbuf *m;
28138414Smckusick 	register struct nfsreq *rep;
28238414Smckusick 	register int error = 0, s;
28338414Smckusick 	struct mbuf *nextrecord;
28439344Smckusick 	struct sockaddr_in *saddr;
28539344Smckusick 	u_long inaddr;
28638414Smckusick 	struct rpc_replyhead replyh;
28738414Smckusick 	struct mbuf *mp;
28838414Smckusick 	char *cp;
28938414Smckusick 	int cnt, xfer;
29038414Smckusick 	int found;
29138414Smckusick 
29238414Smckusick restart:
29339344Smckusick 	nfs_sblock(&so->so_rcv);
29438414Smckusick 	/* Already received, bye bye */
29539344Smckusick 	if (myrep->r_mrep != NULL) {
29639344Smckusick 		sbunlock(&so->so_rcv);
29738414Smckusick 		return (0);
29839344Smckusick 	}
29938414Smckusick 	/* If a soft mount and we have run out of retries */
30039344Smckusick 	if (myrep->r_retry == 0 && myrep->r_timer == 0) {
30139344Smckusick 		sbunlock(&so->so_rcv);
30238414Smckusick 		return (ETIMEDOUT);
30339344Smckusick 	}
30438414Smckusick 	s = splnet();
30538414Smckusick 
30639344Smckusick 	m = so->so_rcv.sb_mb;
30739344Smckusick 	if (m == 0) {
30839344Smckusick 		if (so->so_rcv.sb_cc)
30939344Smckusick 			panic("nfs_soreply 1");
31038414Smckusick 		if (so->so_error) {
31138414Smckusick 			error = so->so_error;
31238414Smckusick 			so->so_error = 0;
31338414Smckusick 			goto release;
31438414Smckusick 		}
31538414Smckusick 		if (so->so_state & SS_CANTRCVMORE)
31638414Smckusick 			goto release;
31738414Smckusick 		sbunlock(&so->so_rcv);
31838414Smckusick 		nfs_sbwait(&so->so_rcv);
31938414Smckusick 		splx(s);
32038414Smckusick 		goto restart;
32138414Smckusick 	}
32238414Smckusick 	nextrecord = m->m_nextpkt;
32338414Smckusick 
32438414Smckusick 	/*
32538414Smckusick 	 * Take off the address, check for rights and ditch any control
32638414Smckusick 	 * mbufs.
32738414Smckusick 	 */
32838414Smckusick 	if (m->m_type != MT_SONAME)
32938414Smckusick 		panic("nfs reply SONAME");
33039344Smckusick 	saddr = mtod(m, struct sockaddr_in *);
33139344Smckusick 	inaddr = saddr->sin_addr.s_addr;
33238414Smckusick 	sbfree(&so->so_rcv, m);
33338414Smckusick 	MFREE(m, so->so_rcv.sb_mb);
33438414Smckusick 	m = so->so_rcv.sb_mb;
33538414Smckusick 	if (m && m->m_type == MT_RIGHTS)
33638414Smckusick 		panic("nfs reply RIGHTS");
33738414Smckusick 	if (m && m->m_type == MT_CONTROL) {
33838414Smckusick 		sbfree(&so->so_rcv, m);
33938414Smckusick 		MFREE(m, so->so_rcv.sb_mb);
34038414Smckusick 		m = so->so_rcv.sb_mb;
34138414Smckusick 	}
34239344Smckusick 	if (m) {
34338414Smckusick 		m->m_nextpkt = nextrecord;
34439344Smckusick 	} else {
34539344Smckusick 		so->so_rcv.sb_mb = nextrecord;
34638414Smckusick 		sbunlock(&so->so_rcv);
34738414Smckusick 		splx(s);
34838414Smckusick 		goto restart;
34938414Smckusick 	}
35038414Smckusick 
35138414Smckusick 	/*
35238414Smckusick 	 * Get the xid and check that it is an rpc reply
35338414Smckusick 	 */
35438414Smckusick 	mp = m;
35538414Smckusick 	if (m->m_len >= 2*NFSX_UNSIGNED)
35638414Smckusick 		bcopy(mtod(m, caddr_t), (caddr_t)&replyh, 2*NFSX_UNSIGNED);
35738414Smckusick 	else {
35838414Smckusick 		cnt = 2*NFSX_UNSIGNED;
35938414Smckusick 		cp = (caddr_t)&replyh;
36038414Smckusick 		while (mp && cnt > 0) {
36138414Smckusick 			if (mp->m_len > 0) {
36238414Smckusick 				xfer = (mp->m_len >= cnt) ? cnt : mp->m_len;
36338414Smckusick 				bcopy(mtod(mp, caddr_t), cp, xfer);
36438414Smckusick 				cnt -= xfer;
36538414Smckusick 				cp += xfer;
36638414Smckusick 			}
36738414Smckusick 			if (cnt > 0)
36838414Smckusick 				mp = mp->m_next;
36938414Smckusick 		}
37038414Smckusick 	}
37139344Smckusick 	found = 0;
37238414Smckusick 	if (replyh.r_rep != rpc_reply || mp == NULL)
37338414Smckusick 		goto dropit;
37438414Smckusick 	/*
37538414Smckusick 	 * Loop through the request list to match up the reply
37638414Smckusick 	 * Iff no match, just drop the datagram
37738414Smckusick 	 */
37839344Smckusick 	rep = nfsreqh.r_next;
37939344Smckusick 	while (!found && rep != &nfsreqh) {
38039344Smckusick 		if (rep->r_mrep == NULL && replyh.r_xid == rep->r_xid &&
38139344Smckusick 		    inaddr == rep->r_inaddr) {
38238414Smckusick 			/* Found it.. */
38338414Smckusick 			rep->r_mrep = m;
38438414Smckusick 			while (m) {
38538414Smckusick 				if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
38638414Smckusick 					panic("nfs_soreply 3");
38738414Smckusick 				sbfree(&so->so_rcv, m);
38838414Smckusick 				m = so->so_rcv.sb_mb = m->m_next;
38938414Smckusick 			}
39038414Smckusick 			so->so_rcv.sb_mb = nextrecord;
39138414Smckusick 			if (rep == myrep)
39238414Smckusick 				goto release;
39338414Smckusick 			found++;
39438414Smckusick 		}
39538414Smckusick 		rep = rep->r_next;
39638414Smckusick 	}
39738414Smckusick 	/* Iff not matched to request, drop it */
39838414Smckusick dropit:
39939344Smckusick 	if (!found) {
40038414Smckusick 		sbdroprecord(&so->so_rcv);
40139344Smckusick 	} else if (so->so_rcv.sb_flags & SB_WAIT) {
40239344Smckusick 		so->so_rcv.sb_flags &= ~SB_WAIT;
40339344Smckusick 		wakeup((caddr_t)&so->so_rcv.sb_cc);
40439344Smckusick 	}
40538414Smckusick 	sbunlock(&so->so_rcv);
40638414Smckusick 	splx(s);
40738414Smckusick 	goto restart;
40838414Smckusick release:
40938414Smckusick 	sbunlock(&so->so_rcv);
41038414Smckusick 	splx(s);
41138414Smckusick 	return (error);
41238414Smckusick }
41338414Smckusick 
41438414Smckusick /*
41538414Smckusick  * nfs_request - goes something like this
41638414Smckusick  *	- fill in request struct
41738414Smckusick  *	- links it into list
41838414Smckusick  *	- calls nfs_sosend() for first transmit
41938414Smckusick  *	- calls nfs_soreceive() to get reply
42038414Smckusick  *	- break down rpc header and return with nfs reply pointed to
42138414Smckusick  *	  by mrep or error
42238414Smckusick  * nb: always frees up mreq mbuf list
42338414Smckusick  */
42438414Smckusick nfs_request(vp, mreq, xid, mp, mrp, mdp, dposp)
42538414Smckusick 	struct vnode *vp;
42638414Smckusick 	struct mbuf *mreq;
42738414Smckusick 	u_long xid;
42838414Smckusick 	struct mount *mp;
42938414Smckusick 	struct mbuf **mrp;
43038414Smckusick 	struct mbuf **mdp;
43138414Smckusick 	caddr_t *dposp;
43238414Smckusick {
43338414Smckusick 	register struct mbuf *m, *mrep;
43438414Smckusick 	register struct nfsreq *rep;
43538414Smckusick 	register u_long *p;
43638414Smckusick 	register int len;
43738414Smckusick 	struct nfsmount *mntp;
43838414Smckusick 	struct mbuf *md;
43939344Smckusick 	struct sockaddr_in *saddr;
44039344Smckusick 	struct nfsreq *reph;
44138414Smckusick 	caddr_t dpos;
44238414Smckusick 	char *cp2;
44338414Smckusick 	int t1;
44438414Smckusick 	int s;
44538414Smckusick 	int error;
44638414Smckusick 
44738414Smckusick 	mntp = vfs_to_nfs(mp);
44838414Smckusick 	m = mreq;
44938414Smckusick 	MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
45038414Smckusick 	rep->r_xid = xid;
45138414Smckusick 	rep->r_mntp = mntp;
45239344Smckusick 	saddr = mtod(mntp->nm_sockaddr, struct sockaddr_in *);
45339344Smckusick 	rep->r_inaddr = saddr->sin_addr.s_addr;
45438414Smckusick 	rep->r_vp = vp;
45538414Smckusick 	if (mntp->nm_flag & NFSMNT_SOFT)
45638414Smckusick 		rep->r_retry = mntp->nm_retrans;
45738414Smckusick 	else
45838414Smckusick 		rep->r_retry = VNOVAL;
45938414Smckusick 	rep->r_mrep = NULL;
46038414Smckusick 	rep->r_mreq = m;
46138414Smckusick 	rep->r_timer = rep->r_timeout = mntp->nm_timeo;
46238414Smckusick 	len = 0;
46338414Smckusick 	while (m) {
46438414Smckusick 		len += m->m_len;
46538414Smckusick 		m = m->m_next;
46638414Smckusick 	}
46738414Smckusick 	rep->r_msiz = len;
46838414Smckusick 	m = NFSMCOPY(mreq, 0, M_COPYALL, M_WAIT);
46938414Smckusick 
47038414Smckusick 	/* Chain it into list of outstanding requests */
47139344Smckusick 	reph = &nfsreqh;
47238414Smckusick 	s = splnet();
47339344Smckusick 	if (reph->r_prev == NULL) {
47439344Smckusick 		reph->r_next = rep;
47539344Smckusick 		rep->r_prev = reph;
47639344Smckusick 	} else {
47739344Smckusick 		reph->r_prev->r_next = rep;
47839344Smckusick 		rep->r_prev = reph->r_prev;
47939344Smckusick 	}
48039344Smckusick 	reph->r_prev = rep;
48139344Smckusick 	rep->r_next = reph;
48238414Smckusick 	splx(s);
48338414Smckusick 
48438414Smckusick 	/*
48538414Smckusick 	 * Iff the NFSMCOPY above succeeded, send it off...
48638414Smckusick 	 * otherwise the timer will retransmit later
48738414Smckusick 	 */
48838414Smckusick 	if (m != NULL)
48938414Smckusick 		error = nfs_udpsend(mntp->nm_so, (struct mbuf *)0, m, 0, len);
49039344Smckusick 	error = nfs_udpreply(mntp->nm_so, mntp, rep);
49138414Smckusick 
49238414Smckusick 	s = splnet();
49338414Smckusick 	rep->r_prev->r_next = rep->r_next;
49439344Smckusick 	rep->r_next->r_prev = rep->r_prev;
49538414Smckusick 	splx(s);
49638414Smckusick 	m_freem(rep->r_mreq);
49738414Smckusick 	mrep = md = rep->r_mrep;
49838414Smckusick 	FREE((caddr_t)rep, M_NFSREQ);
49938414Smckusick 	if (error)
50038414Smckusick 		return (error);
50138414Smckusick 
50238414Smckusick 	/*
50338414Smckusick 	 * break down the rpc header and check if ok
50438414Smckusick 	 */
50538414Smckusick 	dpos = mtod(md, caddr_t);
50638414Smckusick 	nfsm_disect(p, u_long *, 5*NFSX_UNSIGNED);
50738414Smckusick 	p += 2;
50838414Smckusick 	if (*p++ == rpc_msgdenied) {
50938414Smckusick 		if (*p == rpc_mismatch)
51038414Smckusick 			error = EOPNOTSUPP;
51138414Smckusick 		else
51238414Smckusick 			error = EACCES;
51338414Smckusick 		m_freem(mrep);
51438414Smckusick 		return (error);
51538414Smckusick 	}
51638414Smckusick 	/*
51738414Smckusick 	 * skip over the auth_verf, someday we may want to cache auth_short's
51838414Smckusick 	 * for nfs_reqhead(), but for now just dump it
51938414Smckusick 	 */
52038414Smckusick 	if (*++p != 0) {
52138414Smckusick 		len = nfsm_rndup(fxdr_unsigned(long, *p));
52238414Smckusick 		nfsm_adv(len);
52338414Smckusick 	}
52438414Smckusick 	nfsm_disect(p, u_long *, NFSX_UNSIGNED);
52538414Smckusick 	/* 0 == ok */
52638414Smckusick 	if (*p == 0) {
52738414Smckusick 		nfsm_disect(p, u_long *, NFSX_UNSIGNED);
52838414Smckusick 		if (*p != 0) {
52938414Smckusick 			error = fxdr_unsigned(int, *p);
53038414Smckusick 			m_freem(mrep);
53138414Smckusick 			return (error);
53238414Smckusick 		}
53338414Smckusick 		*mrp = mrep;
53438414Smckusick 		*mdp = md;
53538414Smckusick 		*dposp = dpos;
53638414Smckusick 		return (0);
53738414Smckusick 	}
53838414Smckusick 	m_freem(mrep);
53938414Smckusick 	return (EPROTONOSUPPORT);
54038414Smckusick nfsmout:
54138414Smckusick 	return (error);
54238414Smckusick }
54338414Smckusick 
54438414Smckusick /*
54538414Smckusick  * Get a request for the server main loop
54638414Smckusick  * - receive a request via. nfs_soreceive()
54738414Smckusick  * - verify it
54838414Smckusick  * - fill in the cred struct.
54938414Smckusick  */
550*39754Smckusick nfs_getreq(so, prog, vers, maxproc, nam, mrp, mdp, dposp, retxid, proc, cr,
551*39754Smckusick 	   msk, mtch)
55238414Smckusick 	struct socket *so;
55338414Smckusick 	u_long prog;
55438414Smckusick 	u_long vers;
55538414Smckusick 	int maxproc;
55638414Smckusick 	struct mbuf **nam;
55738414Smckusick 	struct mbuf **mrp;
55838414Smckusick 	struct mbuf **mdp;
55938414Smckusick 	caddr_t *dposp;
56038414Smckusick 	u_long *retxid;
56138414Smckusick 	u_long *proc;
56238414Smckusick 	register struct ucred *cr;
563*39754Smckusick 	u_long msk;
564*39754Smckusick 	u_long mtch;
56538414Smckusick {
56638414Smckusick 	register int i;
56739494Smckusick 	register u_long *p;
56839494Smckusick 	register long t1;
56939494Smckusick 	caddr_t dpos, cp2;
57039494Smckusick 	int error = 0;
57139494Smckusick 	struct mbuf *mrep, *md;
57239494Smckusick 	int len;
57338414Smckusick 
574*39754Smckusick 	if (error = nfs_udpreceive(so, msk, mtch, nam, &mrep))
57538414Smckusick 		return (error);
57638414Smckusick 	md = mrep;
57738414Smckusick 	dpos = mtod(mrep, caddr_t);
57838414Smckusick 	nfsm_disect(p, u_long *, 10*NFSX_UNSIGNED);
57938414Smckusick 	*retxid = *p++;
58038414Smckusick 	if (*p++ != rpc_call) {
58138414Smckusick 		m_freem(mrep);
58238414Smckusick 		return (ERPCMISMATCH);
58338414Smckusick 	}
58438414Smckusick 	if (*p++ != rpc_vers) {
58538414Smckusick 		m_freem(mrep);
58638414Smckusick 		return (ERPCMISMATCH);
58738414Smckusick 	}
58838414Smckusick 	if (*p++ != prog) {
58938414Smckusick 		m_freem(mrep);
59038414Smckusick 		return (EPROGUNAVAIL);
59138414Smckusick 	}
59238414Smckusick 	if (*p++ != vers) {
59338414Smckusick 		m_freem(mrep);
59438414Smckusick 		return (EPROGMISMATCH);
59538414Smckusick 	}
59638414Smckusick 	*proc = fxdr_unsigned(u_long, *p++);
59738414Smckusick 	if (*proc == NFSPROC_NULL) {
59838414Smckusick 		*mrp = mrep;
59938414Smckusick 		return (0);
60038414Smckusick 	}
60138414Smckusick 	if (*proc > maxproc || *p++ != rpc_auth_unix) {
60238414Smckusick 		m_freem(mrep);
60338414Smckusick 		return (EPROCUNAVAIL);
60438414Smckusick 	}
60539494Smckusick 	(void) fxdr_unsigned(int, *p++);
60639494Smckusick 	len = fxdr_unsigned(int, *++p);
60739494Smckusick 	nfsm_adv(nfsm_rndup(len));
60838414Smckusick 	nfsm_disect(p, u_long *, 3*NFSX_UNSIGNED);
60938414Smckusick 	cr->cr_uid = fxdr_unsigned(uid_t, *p++);
61038414Smckusick 	cr->cr_gid = fxdr_unsigned(gid_t, *p++);
61139494Smckusick 	len = fxdr_unsigned(int, *p);
61239494Smckusick 	if (len > 10) {
61338414Smckusick 		m_freem(mrep);
61438414Smckusick 		return (EBADRPC);
61538414Smckusick 	}
61639494Smckusick 	nfsm_disect(p, u_long *, (len + 2)*NFSX_UNSIGNED);
61739494Smckusick 	for (i = 1; i <= len; i++)
61838414Smckusick 		cr->cr_groups[i] = fxdr_unsigned(gid_t, *p++);
61939494Smckusick 	cr->cr_ngroups = len + 1;
62038414Smckusick 	/*
62138414Smckusick 	 * Do we have any use for the verifier.
62238414Smckusick 	 * According to the "Remote Procedure Call Protocol Spec." it
62338414Smckusick 	 * should be AUTH_NULL, but some clients make it AUTH_UNIX?
62438414Smckusick 	 * For now, just skip over it
62538414Smckusick 	 */
62639494Smckusick 	len = fxdr_unsigned(int, *++p);
62739494Smckusick 	if (len > 0)
62839494Smckusick 		nfsm_adv(nfsm_rndup(len));
62938414Smckusick 	*mrp = mrep;
63038414Smckusick 	*mdp = md;
63138414Smckusick 	*dposp = dpos;
63238414Smckusick 	return (0);
63338414Smckusick nfsmout:
63438414Smckusick 	return (error);
63538414Smckusick }
63638414Smckusick 
63738414Smckusick /*
63838414Smckusick  * Generate the rpc reply header
63938414Smckusick  * siz arg. is used to decide if adding a cluster is worthwhile
64038414Smckusick  */
64138414Smckusick nfs_rephead(siz, retxid, err, mrq, mbp, bposp)
64238414Smckusick 	int siz;
64338414Smckusick 	u_long retxid;
64438414Smckusick 	int err;
64538414Smckusick 	struct mbuf **mrq;
64638414Smckusick 	struct mbuf **mbp;
64738414Smckusick 	caddr_t *bposp;
64838414Smckusick {
64939494Smckusick 	register u_long *p;
65039494Smckusick 	register long t1;
65139494Smckusick 	caddr_t bpos;
65239494Smckusick 	struct mbuf *mreq, *mb, *mb2;
65338414Smckusick 
65438414Smckusick 	NFSMGETHDR(mreq);
65538414Smckusick 	mb = mreq;
65638414Smckusick 	if ((siz+RPC_REPLYSIZ) > MHLEN)
65738414Smckusick 		NFSMCLGET(mreq, M_WAIT);
65838414Smckusick 	p = mtod(mreq, u_long *);
65938414Smckusick 	mreq->m_len = 6*NFSX_UNSIGNED;
66038414Smckusick 	bpos = ((caddr_t)p)+mreq->m_len;
66138414Smckusick 	*p++ = retxid;
66238414Smckusick 	*p++ = rpc_reply;
66338414Smckusick 	if (err == ERPCMISMATCH) {
66438414Smckusick 		*p++ = rpc_msgdenied;
66538414Smckusick 		*p++ = rpc_mismatch;
66638414Smckusick 		*p++ = txdr_unsigned(2);
66738414Smckusick 		*p = txdr_unsigned(2);
66838414Smckusick 	} else {
66938414Smckusick 		*p++ = rpc_msgaccepted;
67038414Smckusick 		*p++ = 0;
67138414Smckusick 		*p++ = 0;
67238414Smckusick 		switch (err) {
67338414Smckusick 		case EPROGUNAVAIL:
67438414Smckusick 			*p = txdr_unsigned(RPC_PROGUNAVAIL);
67538414Smckusick 			break;
67638414Smckusick 		case EPROGMISMATCH:
67738414Smckusick 			*p = txdr_unsigned(RPC_PROGMISMATCH);
67838414Smckusick 			nfsm_build(p, u_long *, 2*NFSX_UNSIGNED);
67938414Smckusick 			*p++ = txdr_unsigned(2);
68038414Smckusick 			*p = txdr_unsigned(2);	/* someday 3 */
68138414Smckusick 			break;
68238414Smckusick 		case EPROCUNAVAIL:
68338414Smckusick 			*p = txdr_unsigned(RPC_PROCUNAVAIL);
68438414Smckusick 			break;
68538414Smckusick 		default:
68638414Smckusick 			*p = 0;
68738414Smckusick 			if (err != VNOVAL) {
68838414Smckusick 				nfsm_build(p, u_long *, NFSX_UNSIGNED);
68938414Smckusick 				*p = txdr_unsigned(err);
69038414Smckusick 			}
69138414Smckusick 			break;
69238414Smckusick 		};
69338414Smckusick 	}
69438414Smckusick 	*mrq = mreq;
69538414Smckusick 	*mbp = mb;
69638414Smckusick 	*bposp = bpos;
69738414Smckusick 	if (err != 0 && err != VNOVAL)
69838414Smckusick 		nfsstats.srvrpc_errs++;
69938414Smckusick 	return (0);
70038414Smckusick }
70138414Smckusick 
70238414Smckusick /*
70338414Smckusick  * Nfs timer routine
70438414Smckusick  * Scan the nfsreq list and retranmit any requests that have timed out
70538414Smckusick  * To avoid retransmission attempts on STREAM sockets (in the future) make
70638414Smckusick  * sure to set the r_retry field to 0.
70738414Smckusick  */
70838414Smckusick nfs_timer()
70938414Smckusick {
71038414Smckusick 	register struct nfsreq *rep;
71138414Smckusick 	register struct mbuf *m;
71238414Smckusick 	register struct socket *so;
71339494Smckusick 	int s;
71438414Smckusick 
71538414Smckusick 	s = splnet();
71638414Smckusick 	rep = nfsreqh.r_next;
71739344Smckusick 	while (rep && rep != &nfsreqh) {
71838414Smckusick 		if (rep->r_timer > 0)
71938414Smckusick 			rep->r_timer--;
72038414Smckusick 		else if (rep->r_mrep == NULL && rep->r_retry > 0) {
72138414Smckusick 			so = rep->r_mntp->nm_so;
72238414Smckusick 			if ((so->so_state & SS_CANTSENDMORE) == 0 &&
72338414Smckusick 			    !so->so_error &&
72438414Smckusick 			    sbspace(&so->so_snd) >= rep->r_msiz) {
72538414Smckusick 				m = NFSMCOPY(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT);
72638414Smckusick 				if (m != NULL) {
72738414Smckusick 					nfsstats.rpcretries++;
72838425Smckusick 					rep->r_timeout <<= 2; /* x4 backoff */
72938425Smckusick 					if (rep->r_timeout > NFS_MAXTIMEO)
73038425Smckusick 						rep->r_timeout = NFS_MAXTIMEO;
73138414Smckusick 					rep->r_timer = rep->r_timeout;
73238414Smckusick 					if (rep->r_retry != VNOVAL)
73338414Smckusick 						rep->r_retry--;
73438414Smckusick #ifdef MGETHDR
73538414Smckusick 					m->m_pkthdr.len = rep->r_msiz;
73638414Smckusick #endif
73738414Smckusick 					(*so->so_proto->pr_usrreq)(so, PRU_SEND,
73838414Smckusick 						m, (caddr_t)0, (struct mbuf *)0,
73938414Smckusick 						(struct mbuf *)0);
74038414Smckusick 				}
74138414Smckusick 			}
74238414Smckusick 		}
74338414Smckusick 		rep = rep->r_next;
74438414Smckusick 	}
74538414Smckusick 	splx(s);
74638425Smckusick 	timeout(nfs_timer, (caddr_t)0, hz/10);
74738414Smckusick }
74838414Smckusick 
74938414Smckusick /*
75038414Smckusick  * nfs_sbwait() is simply sbwait() but at a negative priority so that it
75138414Smckusick  * can not be interrupted by a signal.
75238414Smckusick  */
75338414Smckusick nfs_sbwait(sb)
75438414Smckusick 	struct sockbuf *sb;
75538414Smckusick {
75638414Smckusick 	sb->sb_flags |= SB_WAIT;
75739344Smckusick 	sleep((caddr_t)&sb->sb_cc, PZERO-2);
75838414Smckusick }
759