xref: /csrg-svn/sys/nfs/nfs_socket.c (revision 39494)
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*39494Smckusick  *	@(#)nfs_socket.c	7.4 (Berkeley) 11/03/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
2838414Smckusick  *  stream such as Sun's RM (Section 3.2 of the Sun RPC Message Protocol
2938414Smckusick  *  manual, in Networking on the Sun Workstation, Part #800-1324-03
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;
124*39494Smckusick 	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  */
17038414Smckusick nfs_udpreceive(so, aname, mp)
17138414Smckusick 	register struct socket *so;
17238414Smckusick 	struct mbuf **aname;
17338414Smckusick 	struct mbuf **mp;
17438414Smckusick {
17538414Smckusick 	register struct mbuf *m;
17638414Smckusick 	int s, error = 0;
17738414Smckusick 	struct mbuf *nextrecord;
17838414Smckusick 
17938414Smckusick 	if (aname)
18038414Smckusick 		*aname = 0;
18138414Smckusick 
18238414Smckusick restart:
18338414Smckusick 	sblock(&so->so_rcv);
18438414Smckusick 	s = splnet();
18538414Smckusick 
18638414Smckusick 	if (so->so_rcv.sb_cc == 0) {
18738414Smckusick 		if (so->so_error) {
18838414Smckusick 			error = so->so_error;
18938414Smckusick 			so->so_error = 0;
19038414Smckusick 			goto release;
19138414Smckusick 		}
19238414Smckusick 		if (so->so_state & SS_CANTRCVMORE)
19338414Smckusick 			goto release;
19438414Smckusick 		sbunlock(&so->so_rcv);
19538414Smckusick 		sbwait(&so->so_rcv);
19638414Smckusick 		splx(s);
19738414Smckusick 		goto restart;
19838414Smckusick 	}
19938414Smckusick 	m = so->so_rcv.sb_mb;
20038414Smckusick 	if (m == 0)
20138414Smckusick 		panic("nfs_receive 1");
20238414Smckusick 	nextrecord = m->m_nextpkt;
20338414Smckusick 	if (m->m_type != MT_SONAME)
20438414Smckusick 		panic("nfs_receive 1a");
20538414Smckusick 	sbfree(&so->so_rcv, m);
20638414Smckusick 	if (aname) {
20738414Smckusick 		*aname = m;
20838414Smckusick 		so->so_rcv.sb_mb = m->m_next;
20938414Smckusick 		m->m_next = 0;
21038414Smckusick 		m = so->so_rcv.sb_mb;
21138414Smckusick 	} else {
21238414Smckusick 		MFREE(m, so->so_rcv.sb_mb);
21338414Smckusick 		m = so->so_rcv.sb_mb;
21438414Smckusick 	}
21538414Smckusick 	if (m && m->m_type == MT_RIGHTS)
21638414Smckusick 		panic("nfs_receive 2");
21738414Smckusick 	if (m && m->m_type == MT_CONTROL) {
21838414Smckusick 		sbfree(&so->so_rcv, m);
21938414Smckusick 		MFREE(m, so->so_rcv.sb_mb);
22038414Smckusick 		m = so->so_rcv.sb_mb;
22138414Smckusick 	}
22238414Smckusick 	*mp = m;
22338414Smckusick 	while (m) {
22438414Smckusick 		if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
22538414Smckusick 			panic("nfs_receive 3");
22638414Smckusick 		sbfree(&so->so_rcv, m);
22738414Smckusick 		m = so->so_rcv.sb_mb = m->m_next;
22838414Smckusick 	}
22938414Smckusick 	so->so_rcv.sb_mb = nextrecord;
23038414Smckusick 	so->so_state &= ~SS_RCVATMARK;	/* Necessary ?? */
23138414Smckusick release:
23238414Smckusick 	sbunlock(&so->so_rcv);
23338414Smckusick 	splx(s);
23438414Smckusick 	return (error);
23538414Smckusick }
23638414Smckusick 
23738414Smckusick struct nfsreq nfsreqh = {
23838414Smckusick 	(struct nfsreq *)0,
23938414Smckusick 	(struct nfsreq *)0,
24038414Smckusick 	(struct mbuf *)0,
24138414Smckusick 	(struct mbuf *)0,
24238414Smckusick 	(struct nfsmount *)0,
24338414Smckusick 	0, 0, 0, 0, 0,
24438414Smckusick };
24538414Smckusick 
24638414Smckusick struct rpc_replyhead {
24738414Smckusick 	u_long	r_xid;
24838414Smckusick 	u_long	r_rep;
24938414Smckusick };
25038414Smckusick 
25138414Smckusick /*
25238414Smckusick  * Implement receipt of reply on a socket.
25338414Smckusick  * We depend on the way that records are added to the sockbuf
25438414Smckusick  * by sbappend*.  In particular, each record (mbufs linked through m_next)
25538414Smckusick  * must begin with an address, followed by optional MT_CONTROL mbuf
25638414Smckusick  * and then zero or more mbufs of data.
25738414Smckusick  * Although the sockbuf is locked, new data may still be appended,
25838414Smckusick  * and thus we must maintain consistency of the sockbuf during that time.
25938414Smckusick  * We must search through the list of received datagrams matching them
26038414Smckusick  * with outstanding requests using the xid, until ours is found.
26138414Smckusick  */
262*39494Smckusick /* ARGSUSED */
26339344Smckusick nfs_udpreply(so, mntp, myrep)
26438414Smckusick 	register struct socket *so;
26538414Smckusick 	struct nfsmount *mntp;
26639344Smckusick 	struct nfsreq *myrep;
26738414Smckusick {
26838414Smckusick 	register struct mbuf *m;
26938414Smckusick 	register struct nfsreq *rep;
27038414Smckusick 	register int error = 0, s;
27138414Smckusick 	struct mbuf *nextrecord;
27239344Smckusick 	struct sockaddr_in *saddr;
27339344Smckusick 	u_long inaddr;
27438414Smckusick 	struct rpc_replyhead replyh;
27538414Smckusick 	struct mbuf *mp;
27638414Smckusick 	char *cp;
27738414Smckusick 	int cnt, xfer;
27838414Smckusick 	int found;
27938414Smckusick 
28038414Smckusick restart:
28139344Smckusick 	nfs_sblock(&so->so_rcv);
28238414Smckusick 	/* Already received, bye bye */
28339344Smckusick 	if (myrep->r_mrep != NULL) {
28439344Smckusick 		sbunlock(&so->so_rcv);
28538414Smckusick 		return (0);
28639344Smckusick 	}
28738414Smckusick 	/* If a soft mount and we have run out of retries */
28839344Smckusick 	if (myrep->r_retry == 0 && myrep->r_timer == 0) {
28939344Smckusick 		sbunlock(&so->so_rcv);
29038414Smckusick 		return (ETIMEDOUT);
29139344Smckusick 	}
29238414Smckusick 	s = splnet();
29338414Smckusick 
29439344Smckusick 	m = so->so_rcv.sb_mb;
29539344Smckusick 	if (m == 0) {
29639344Smckusick 		if (so->so_rcv.sb_cc)
29739344Smckusick 			panic("nfs_soreply 1");
29838414Smckusick 		if (so->so_error) {
29938414Smckusick 			error = so->so_error;
30038414Smckusick 			so->so_error = 0;
30138414Smckusick 			goto release;
30238414Smckusick 		}
30338414Smckusick 		if (so->so_state & SS_CANTRCVMORE)
30438414Smckusick 			goto release;
30538414Smckusick 		sbunlock(&so->so_rcv);
30638414Smckusick 		nfs_sbwait(&so->so_rcv);
30738414Smckusick 		splx(s);
30838414Smckusick 		goto restart;
30938414Smckusick 	}
31038414Smckusick 	nextrecord = m->m_nextpkt;
31138414Smckusick 
31238414Smckusick 	/*
31338414Smckusick 	 * Take off the address, check for rights and ditch any control
31438414Smckusick 	 * mbufs.
31538414Smckusick 	 */
31638414Smckusick 	if (m->m_type != MT_SONAME)
31738414Smckusick 		panic("nfs reply SONAME");
31839344Smckusick 	saddr = mtod(m, struct sockaddr_in *);
31939344Smckusick 	inaddr = saddr->sin_addr.s_addr;
32038414Smckusick 	sbfree(&so->so_rcv, m);
32138414Smckusick 	MFREE(m, so->so_rcv.sb_mb);
32238414Smckusick 	m = so->so_rcv.sb_mb;
32338414Smckusick 	if (m && m->m_type == MT_RIGHTS)
32438414Smckusick 		panic("nfs reply RIGHTS");
32538414Smckusick 	if (m && m->m_type == MT_CONTROL) {
32638414Smckusick 		sbfree(&so->so_rcv, m);
32738414Smckusick 		MFREE(m, so->so_rcv.sb_mb);
32838414Smckusick 		m = so->so_rcv.sb_mb;
32938414Smckusick 	}
33039344Smckusick 	if (m) {
33138414Smckusick 		m->m_nextpkt = nextrecord;
33239344Smckusick 	} else {
33339344Smckusick 		so->so_rcv.sb_mb = nextrecord;
33438414Smckusick 		sbunlock(&so->so_rcv);
33538414Smckusick 		splx(s);
33638414Smckusick 		goto restart;
33738414Smckusick 	}
33838414Smckusick 
33938414Smckusick 	/*
34038414Smckusick 	 * Get the xid and check that it is an rpc reply
34138414Smckusick 	 */
34238414Smckusick 	mp = m;
34338414Smckusick 	if (m->m_len >= 2*NFSX_UNSIGNED)
34438414Smckusick 		bcopy(mtod(m, caddr_t), (caddr_t)&replyh, 2*NFSX_UNSIGNED);
34538414Smckusick 	else {
34638414Smckusick 		cnt = 2*NFSX_UNSIGNED;
34738414Smckusick 		cp = (caddr_t)&replyh;
34838414Smckusick 		while (mp && cnt > 0) {
34938414Smckusick 			if (mp->m_len > 0) {
35038414Smckusick 				xfer = (mp->m_len >= cnt) ? cnt : mp->m_len;
35138414Smckusick 				bcopy(mtod(mp, caddr_t), cp, xfer);
35238414Smckusick 				cnt -= xfer;
35338414Smckusick 				cp += xfer;
35438414Smckusick 			}
35538414Smckusick 			if (cnt > 0)
35638414Smckusick 				mp = mp->m_next;
35738414Smckusick 		}
35838414Smckusick 	}
35939344Smckusick 	found = 0;
36038414Smckusick 	if (replyh.r_rep != rpc_reply || mp == NULL)
36138414Smckusick 		goto dropit;
36238414Smckusick 	/*
36338414Smckusick 	 * Loop through the request list to match up the reply
36438414Smckusick 	 * Iff no match, just drop the datagram
36538414Smckusick 	 */
36639344Smckusick 	rep = nfsreqh.r_next;
36739344Smckusick 	while (!found && rep != &nfsreqh) {
36839344Smckusick 		if (rep->r_mrep == NULL && replyh.r_xid == rep->r_xid &&
36939344Smckusick 		    inaddr == rep->r_inaddr) {
37038414Smckusick 			/* Found it.. */
37138414Smckusick 			rep->r_mrep = m;
37238414Smckusick 			while (m) {
37338414Smckusick 				if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
37438414Smckusick 					panic("nfs_soreply 3");
37538414Smckusick 				sbfree(&so->so_rcv, m);
37638414Smckusick 				m = so->so_rcv.sb_mb = m->m_next;
37738414Smckusick 			}
37838414Smckusick 			so->so_rcv.sb_mb = nextrecord;
37938414Smckusick 			if (rep == myrep)
38038414Smckusick 				goto release;
38138414Smckusick 			found++;
38238414Smckusick 		}
38338414Smckusick 		rep = rep->r_next;
38438414Smckusick 	}
38538414Smckusick 	/* Iff not matched to request, drop it */
38638414Smckusick dropit:
38739344Smckusick 	if (!found) {
38838414Smckusick 		sbdroprecord(&so->so_rcv);
38939344Smckusick 	} else if (so->so_rcv.sb_flags & SB_WAIT) {
39039344Smckusick 		so->so_rcv.sb_flags &= ~SB_WAIT;
39139344Smckusick 		wakeup((caddr_t)&so->so_rcv.sb_cc);
39239344Smckusick 	}
39338414Smckusick 	sbunlock(&so->so_rcv);
39438414Smckusick 	splx(s);
39538414Smckusick 	goto restart;
39638414Smckusick release:
39738414Smckusick 	sbunlock(&so->so_rcv);
39838414Smckusick 	splx(s);
39938414Smckusick 	return (error);
40038414Smckusick }
40138414Smckusick 
40238414Smckusick /*
40338414Smckusick  * nfs_request - goes something like this
40438414Smckusick  *	- fill in request struct
40538414Smckusick  *	- links it into list
40638414Smckusick  *	- calls nfs_sosend() for first transmit
40738414Smckusick  *	- calls nfs_soreceive() to get reply
40838414Smckusick  *	- break down rpc header and return with nfs reply pointed to
40938414Smckusick  *	  by mrep or error
41038414Smckusick  * nb: always frees up mreq mbuf list
41138414Smckusick  */
41238414Smckusick nfs_request(vp, mreq, xid, mp, mrp, mdp, dposp)
41338414Smckusick 	struct vnode *vp;
41438414Smckusick 	struct mbuf *mreq;
41538414Smckusick 	u_long xid;
41638414Smckusick 	struct mount *mp;
41738414Smckusick 	struct mbuf **mrp;
41838414Smckusick 	struct mbuf **mdp;
41938414Smckusick 	caddr_t *dposp;
42038414Smckusick {
42138414Smckusick 	register struct mbuf *m, *mrep;
42238414Smckusick 	register struct nfsreq *rep;
42338414Smckusick 	register u_long *p;
42438414Smckusick 	register int len;
42538414Smckusick 	struct nfsmount *mntp;
42638414Smckusick 	struct mbuf *md;
42739344Smckusick 	struct sockaddr_in *saddr;
42839344Smckusick 	struct nfsreq *reph;
42938414Smckusick 	caddr_t dpos;
43038414Smckusick 	char *cp2;
43138414Smckusick 	int t1;
43238414Smckusick 	int s;
43338414Smckusick 	int error;
43438414Smckusick 
43538414Smckusick 	mntp = vfs_to_nfs(mp);
43638414Smckusick 	m = mreq;
43738414Smckusick 	MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
43838414Smckusick 	rep->r_xid = xid;
43938414Smckusick 	rep->r_mntp = mntp;
44039344Smckusick 	saddr = mtod(mntp->nm_sockaddr, struct sockaddr_in *);
44139344Smckusick 	rep->r_inaddr = saddr->sin_addr.s_addr;
44238414Smckusick 	rep->r_vp = vp;
44338414Smckusick 	if (mntp->nm_flag & NFSMNT_SOFT)
44438414Smckusick 		rep->r_retry = mntp->nm_retrans;
44538414Smckusick 	else
44638414Smckusick 		rep->r_retry = VNOVAL;
44738414Smckusick 	rep->r_mrep = NULL;
44838414Smckusick 	rep->r_mreq = m;
44938414Smckusick 	rep->r_timer = rep->r_timeout = mntp->nm_timeo;
45038414Smckusick 	len = 0;
45138414Smckusick 	while (m) {
45238414Smckusick 		len += m->m_len;
45338414Smckusick 		m = m->m_next;
45438414Smckusick 	}
45538414Smckusick 	rep->r_msiz = len;
45638414Smckusick 	m = NFSMCOPY(mreq, 0, M_COPYALL, M_WAIT);
45738414Smckusick 
45838414Smckusick 	/* Chain it into list of outstanding requests */
45939344Smckusick 	reph = &nfsreqh;
46038414Smckusick 	s = splnet();
46139344Smckusick 	if (reph->r_prev == NULL) {
46239344Smckusick 		reph->r_next = rep;
46339344Smckusick 		rep->r_prev = reph;
46439344Smckusick 	} else {
46539344Smckusick 		reph->r_prev->r_next = rep;
46639344Smckusick 		rep->r_prev = reph->r_prev;
46739344Smckusick 	}
46839344Smckusick 	reph->r_prev = rep;
46939344Smckusick 	rep->r_next = reph;
47038414Smckusick 	splx(s);
47138414Smckusick 
47238414Smckusick 	/*
47338414Smckusick 	 * Iff the NFSMCOPY above succeeded, send it off...
47438414Smckusick 	 * otherwise the timer will retransmit later
47538414Smckusick 	 */
47638414Smckusick 	if (m != NULL)
47738414Smckusick 		error = nfs_udpsend(mntp->nm_so, (struct mbuf *)0, m, 0, len);
47839344Smckusick 	error = nfs_udpreply(mntp->nm_so, mntp, rep);
47938414Smckusick 
48038414Smckusick 	s = splnet();
48138414Smckusick 	rep->r_prev->r_next = rep->r_next;
48239344Smckusick 	rep->r_next->r_prev = rep->r_prev;
48338414Smckusick 	splx(s);
48438414Smckusick 	m_freem(rep->r_mreq);
48538414Smckusick 	mrep = md = rep->r_mrep;
48638414Smckusick 	FREE((caddr_t)rep, M_NFSREQ);
48738414Smckusick 	if (error)
48838414Smckusick 		return (error);
48938414Smckusick 
49038414Smckusick 	/*
49138414Smckusick 	 * break down the rpc header and check if ok
49238414Smckusick 	 */
49338414Smckusick 	dpos = mtod(md, caddr_t);
49438414Smckusick 	nfsm_disect(p, u_long *, 5*NFSX_UNSIGNED);
49538414Smckusick 	p += 2;
49638414Smckusick 	if (*p++ == rpc_msgdenied) {
49738414Smckusick 		if (*p == rpc_mismatch)
49838414Smckusick 			error = EOPNOTSUPP;
49938414Smckusick 		else
50038414Smckusick 			error = EACCES;
50138414Smckusick 		m_freem(mrep);
50238414Smckusick 		return (error);
50338414Smckusick 	}
50438414Smckusick 	/*
50538414Smckusick 	 * skip over the auth_verf, someday we may want to cache auth_short's
50638414Smckusick 	 * for nfs_reqhead(), but for now just dump it
50738414Smckusick 	 */
50838414Smckusick 	if (*++p != 0) {
50938414Smckusick 		len = nfsm_rndup(fxdr_unsigned(long, *p));
51038414Smckusick 		nfsm_adv(len);
51138414Smckusick 	}
51238414Smckusick 	nfsm_disect(p, u_long *, NFSX_UNSIGNED);
51338414Smckusick 	/* 0 == ok */
51438414Smckusick 	if (*p == 0) {
51538414Smckusick 		nfsm_disect(p, u_long *, NFSX_UNSIGNED);
51638414Smckusick 		if (*p != 0) {
51738414Smckusick 			error = fxdr_unsigned(int, *p);
51838414Smckusick 			m_freem(mrep);
51938414Smckusick 			return (error);
52038414Smckusick 		}
52138414Smckusick 		*mrp = mrep;
52238414Smckusick 		*mdp = md;
52338414Smckusick 		*dposp = dpos;
52438414Smckusick 		return (0);
52538414Smckusick 	}
52638414Smckusick 	m_freem(mrep);
52738414Smckusick 	return (EPROTONOSUPPORT);
52838414Smckusick nfsmout:
52938414Smckusick 	return (error);
53038414Smckusick }
53138414Smckusick 
53238414Smckusick /*
53338414Smckusick  * Get a request for the server main loop
53438414Smckusick  * - receive a request via. nfs_soreceive()
53538414Smckusick  * - verify it
53638414Smckusick  * - fill in the cred struct.
53738414Smckusick  */
53838414Smckusick nfs_getreq(so, prog, vers, maxproc, nam, mrp, mdp, dposp, retxid, proc, cr)
53938414Smckusick 	struct socket *so;
54038414Smckusick 	u_long prog;
54138414Smckusick 	u_long vers;
54238414Smckusick 	int maxproc;
54338414Smckusick 	struct mbuf **nam;
54438414Smckusick 	struct mbuf **mrp;
54538414Smckusick 	struct mbuf **mdp;
54638414Smckusick 	caddr_t *dposp;
54738414Smckusick 	u_long *retxid;
54838414Smckusick 	u_long *proc;
54938414Smckusick 	register struct ucred *cr;
55038414Smckusick {
55138414Smckusick 	register int i;
552*39494Smckusick 	register u_long *p;
553*39494Smckusick 	register long t1;
554*39494Smckusick 	caddr_t dpos, cp2;
555*39494Smckusick 	int error = 0;
556*39494Smckusick 	struct mbuf *mrep, *md;
557*39494Smckusick 	int len;
55838414Smckusick 
55938414Smckusick 	if (error = nfs_udpreceive(so, nam, &mrep))
56038414Smckusick 		return (error);
56138414Smckusick 	md = mrep;
56238414Smckusick 	dpos = mtod(mrep, caddr_t);
56338414Smckusick 	nfsm_disect(p, u_long *, 10*NFSX_UNSIGNED);
56438414Smckusick 	*retxid = *p++;
56538414Smckusick 	if (*p++ != rpc_call) {
56638414Smckusick 		m_freem(mrep);
56738414Smckusick 		return (ERPCMISMATCH);
56838414Smckusick 	}
56938414Smckusick 	if (*p++ != rpc_vers) {
57038414Smckusick 		m_freem(mrep);
57138414Smckusick 		return (ERPCMISMATCH);
57238414Smckusick 	}
57338414Smckusick 	if (*p++ != prog) {
57438414Smckusick 		m_freem(mrep);
57538414Smckusick 		return (EPROGUNAVAIL);
57638414Smckusick 	}
57738414Smckusick 	if (*p++ != vers) {
57838414Smckusick 		m_freem(mrep);
57938414Smckusick 		return (EPROGMISMATCH);
58038414Smckusick 	}
58138414Smckusick 	*proc = fxdr_unsigned(u_long, *p++);
58238414Smckusick 	if (*proc == NFSPROC_NULL) {
58338414Smckusick 		*mrp = mrep;
58438414Smckusick 		return (0);
58538414Smckusick 	}
58638414Smckusick 	if (*proc > maxproc || *p++ != rpc_auth_unix) {
58738414Smckusick 		m_freem(mrep);
58838414Smckusick 		return (EPROCUNAVAIL);
58938414Smckusick 	}
590*39494Smckusick 	(void) fxdr_unsigned(int, *p++);
591*39494Smckusick 	len = fxdr_unsigned(int, *++p);
592*39494Smckusick 	nfsm_adv(nfsm_rndup(len));
59338414Smckusick 	nfsm_disect(p, u_long *, 3*NFSX_UNSIGNED);
59438414Smckusick 	cr->cr_uid = fxdr_unsigned(uid_t, *p++);
59538414Smckusick 	cr->cr_gid = fxdr_unsigned(gid_t, *p++);
596*39494Smckusick 	len = fxdr_unsigned(int, *p);
597*39494Smckusick 	if (len > 10) {
59838414Smckusick 		m_freem(mrep);
59938414Smckusick 		return (EBADRPC);
60038414Smckusick 	}
601*39494Smckusick 	nfsm_disect(p, u_long *, (len + 2)*NFSX_UNSIGNED);
602*39494Smckusick 	for (i = 1; i <= len; i++)
60338414Smckusick 		cr->cr_groups[i] = fxdr_unsigned(gid_t, *p++);
604*39494Smckusick 	cr->cr_ngroups = len + 1;
60538414Smckusick 	/*
60638414Smckusick 	 * Do we have any use for the verifier.
60738414Smckusick 	 * According to the "Remote Procedure Call Protocol Spec." it
60838414Smckusick 	 * should be AUTH_NULL, but some clients make it AUTH_UNIX?
60938414Smckusick 	 * For now, just skip over it
61038414Smckusick 	 */
611*39494Smckusick 	len = fxdr_unsigned(int, *++p);
612*39494Smckusick 	if (len > 0)
613*39494Smckusick 		nfsm_adv(nfsm_rndup(len));
61438414Smckusick 	*mrp = mrep;
61538414Smckusick 	*mdp = md;
61638414Smckusick 	*dposp = dpos;
61738414Smckusick 	return (0);
61838414Smckusick nfsmout:
61938414Smckusick 	return (error);
62038414Smckusick }
62138414Smckusick 
62238414Smckusick /*
62338414Smckusick  * Generate the rpc reply header
62438414Smckusick  * siz arg. is used to decide if adding a cluster is worthwhile
62538414Smckusick  */
62638414Smckusick nfs_rephead(siz, retxid, err, mrq, mbp, bposp)
62738414Smckusick 	int siz;
62838414Smckusick 	u_long retxid;
62938414Smckusick 	int err;
63038414Smckusick 	struct mbuf **mrq;
63138414Smckusick 	struct mbuf **mbp;
63238414Smckusick 	caddr_t *bposp;
63338414Smckusick {
634*39494Smckusick 	register u_long *p;
635*39494Smckusick 	register long t1;
636*39494Smckusick 	caddr_t bpos;
637*39494Smckusick 	struct mbuf *mreq, *mb, *mb2;
63838414Smckusick 
63938414Smckusick 	NFSMGETHDR(mreq);
64038414Smckusick 	mb = mreq;
64138414Smckusick 	if ((siz+RPC_REPLYSIZ) > MHLEN)
64238414Smckusick 		NFSMCLGET(mreq, M_WAIT);
64338414Smckusick 	p = mtod(mreq, u_long *);
64438414Smckusick 	mreq->m_len = 6*NFSX_UNSIGNED;
64538414Smckusick 	bpos = ((caddr_t)p)+mreq->m_len;
64638414Smckusick 	*p++ = retxid;
64738414Smckusick 	*p++ = rpc_reply;
64838414Smckusick 	if (err == ERPCMISMATCH) {
64938414Smckusick 		*p++ = rpc_msgdenied;
65038414Smckusick 		*p++ = rpc_mismatch;
65138414Smckusick 		*p++ = txdr_unsigned(2);
65238414Smckusick 		*p = txdr_unsigned(2);
65338414Smckusick 	} else {
65438414Smckusick 		*p++ = rpc_msgaccepted;
65538414Smckusick 		*p++ = 0;
65638414Smckusick 		*p++ = 0;
65738414Smckusick 		switch (err) {
65838414Smckusick 		case EPROGUNAVAIL:
65938414Smckusick 			*p = txdr_unsigned(RPC_PROGUNAVAIL);
66038414Smckusick 			break;
66138414Smckusick 		case EPROGMISMATCH:
66238414Smckusick 			*p = txdr_unsigned(RPC_PROGMISMATCH);
66338414Smckusick 			nfsm_build(p, u_long *, 2*NFSX_UNSIGNED);
66438414Smckusick 			*p++ = txdr_unsigned(2);
66538414Smckusick 			*p = txdr_unsigned(2);	/* someday 3 */
66638414Smckusick 			break;
66738414Smckusick 		case EPROCUNAVAIL:
66838414Smckusick 			*p = txdr_unsigned(RPC_PROCUNAVAIL);
66938414Smckusick 			break;
67038414Smckusick 		default:
67138414Smckusick 			*p = 0;
67238414Smckusick 			if (err != VNOVAL) {
67338414Smckusick 				nfsm_build(p, u_long *, NFSX_UNSIGNED);
67438414Smckusick 				*p = txdr_unsigned(err);
67538414Smckusick 			}
67638414Smckusick 			break;
67738414Smckusick 		};
67838414Smckusick 	}
67938414Smckusick 	*mrq = mreq;
68038414Smckusick 	*mbp = mb;
68138414Smckusick 	*bposp = bpos;
68238414Smckusick 	if (err != 0 && err != VNOVAL)
68338414Smckusick 		nfsstats.srvrpc_errs++;
68438414Smckusick 	return (0);
68538414Smckusick }
68638414Smckusick 
68738414Smckusick /*
68838414Smckusick  * Nfs timer routine
68938414Smckusick  * Scan the nfsreq list and retranmit any requests that have timed out
69038414Smckusick  * To avoid retransmission attempts on STREAM sockets (in the future) make
69138414Smckusick  * sure to set the r_retry field to 0.
69238414Smckusick  */
69338414Smckusick nfs_timer()
69438414Smckusick {
69538414Smckusick 	register struct nfsreq *rep;
69638414Smckusick 	register struct mbuf *m;
69738414Smckusick 	register struct socket *so;
698*39494Smckusick 	int s;
69938414Smckusick 
70038414Smckusick 	s = splnet();
70138414Smckusick 	rep = nfsreqh.r_next;
70239344Smckusick 	while (rep && rep != &nfsreqh) {
70338414Smckusick 		if (rep->r_timer > 0)
70438414Smckusick 			rep->r_timer--;
70538414Smckusick 		else if (rep->r_mrep == NULL && rep->r_retry > 0) {
70638414Smckusick 			so = rep->r_mntp->nm_so;
70738414Smckusick 			if ((so->so_state & SS_CANTSENDMORE) == 0 &&
70838414Smckusick 			    !so->so_error &&
70938414Smckusick 			    sbspace(&so->so_snd) >= rep->r_msiz) {
71038414Smckusick 				m = NFSMCOPY(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT);
71138414Smckusick 				if (m != NULL) {
71238414Smckusick 					nfsstats.rpcretries++;
71338425Smckusick 					rep->r_timeout <<= 2; /* x4 backoff */
71438425Smckusick 					if (rep->r_timeout > NFS_MAXTIMEO)
71538425Smckusick 						rep->r_timeout = NFS_MAXTIMEO;
71638414Smckusick 					rep->r_timer = rep->r_timeout;
71738414Smckusick 					if (rep->r_retry != VNOVAL)
71838414Smckusick 						rep->r_retry--;
71938414Smckusick #ifdef MGETHDR
72038414Smckusick 					m->m_pkthdr.len = rep->r_msiz;
72138414Smckusick #endif
72238414Smckusick 					(*so->so_proto->pr_usrreq)(so, PRU_SEND,
72338414Smckusick 						m, (caddr_t)0, (struct mbuf *)0,
72438414Smckusick 						(struct mbuf *)0);
72538414Smckusick 				}
72638414Smckusick 			}
72738414Smckusick 		}
72838414Smckusick 		rep = rep->r_next;
72938414Smckusick 	}
73038414Smckusick 	splx(s);
73138425Smckusick 	timeout(nfs_timer, (caddr_t)0, hz/10);
73238414Smckusick }
73338414Smckusick 
73438414Smckusick /*
73538414Smckusick  * nfs_sbwait() is simply sbwait() but at a negative priority so that it
73638414Smckusick  * can not be interrupted by a signal.
73738414Smckusick  */
73838414Smckusick nfs_sbwait(sb)
73938414Smckusick 	struct sockbuf *sb;
74038414Smckusick {
74138414Smckusick 	sb->sb_flags |= SB_WAIT;
74239344Smckusick 	sleep((caddr_t)&sb->sb_cc, PZERO-2);
74338414Smckusick }
744