+ struct proc *p;
+
+ if (rep)
+ p = rep->r_procp;
+ else
+ p = (struct proc *)0;
+ while (*flagp & NFSMNT_SNDLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, p))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTSND;
+ (void) tsleep((caddr_t)flagp, PZERO-1, "nfsndlck", 0);
+ }
+ *flagp |= NFSMNT_SNDLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_sndunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_SNDLOCK) == 0)
+ panic("nfs sndunlock");
+ *flagp &= ~NFSMNT_SNDLOCK;
+ if (*flagp & NFSMNT_WANTSND) {
+ *flagp &= ~NFSMNT_WANTSND;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+nfs_rcvlock(rep)
+ register struct nfsreq *rep;
+{
+ register int *flagp = &rep->r_nmp->nm_flag;
+
+ while (*flagp & NFSMNT_RCVLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, rep->r_procp))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTRCV;
+ (void) tsleep((caddr_t)flagp, PZERO-1, "nfsrcvlck", 0);
+ }
+ *flagp |= NFSMNT_RCVLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_rcvunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_RCVLOCK) == 0)
+ panic("nfs rcvunlock");
+ *flagp &= ~NFSMNT_RCVLOCK;
+ if (*flagp & NFSMNT_WANTRCV) {
+ *flagp &= ~NFSMNT_WANTRCV;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+/*
+ * Check for badly aligned mbuf data areas and
+ * realign data in an mbuf list by copying the data areas up, as required.
+ */
+void
+nfs_realign(m, hsiz)
+ register struct mbuf *m;
+ int hsiz;
+{
+ register struct mbuf *m2;
+ register int siz, mlen, olen;
+ register caddr_t tcp, fcp;
+ struct mbuf *mnew;
+
+ while (m) {
+ /*
+ * This never happens for UDP, rarely happens for TCP
+ * but frequently happens for iso transport.
+ */
+ if ((m->m_len & 0x3) || (mtod(m, int) & 0x3)) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ m->m_flags &= ~M_PKTHDR;
+ if (m->m_flags & M_EXT)
+ m->m_data = m->m_ext.ext_buf;
+ else
+ m->m_data = m->m_dat;
+ m->m_len = 0;
+ tcp = mtod(m, caddr_t);
+ mnew = m;
+ m2 = m->m_next;
+
+ /*
+ * If possible, only put the first invariant part
+ * of the RPC header in the first mbuf.
+ */
+ if (olen <= hsiz)
+ mlen = hsiz;
+ else
+ mlen = M_TRAILINGSPACE(m);
+
+ /*
+ * Loop through the mbuf list consolidating data.
+ */
+ while (m) {
+ while (olen > 0) {
+ if (mlen == 0) {
+ m2->m_flags &= ~M_PKTHDR;
+ if (m2->m_flags & M_EXT)
+ m2->m_data = m2->m_ext.ext_buf;
+ else
+ m2->m_data = m2->m_dat;
+ m2->m_len = 0;
+ mlen = M_TRAILINGSPACE(m2);
+ tcp = mtod(m2, caddr_t);
+ mnew = m2;
+ m2 = m2->m_next;
+ }
+ siz = min(mlen, olen);
+ if (tcp != fcp)
+ bcopy(fcp, tcp, siz);
+ mnew->m_len += siz;
+ mlen -= siz;
+ olen -= siz;
+ tcp += siz;
+ fcp += siz;
+ }
+ m = m->m_next;
+ if (m) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ }
+ }
+
+ /*
+ * Finally, set m_len == 0 for any trailing mbufs that have
+ * been copied out of.
+ */
+ while (m2) {
+ m2->m_len = 0;
+ m2 = m2->m_next;
+ }
+ return;
+ }
+ m = m->m_next;
+ }
+}
+
+/*
+ * Socket upcall routine for the nfsd sockets.
+ * The caddr_t arg is a pointer to the "struct nfssvc_sock".
+ * Essentially do as much as possible non-blocking, else punt and it will
+ * be called with M_WAIT from an nfsd.
+ */
+void
+nfsrv_rcv(so, arg, waitflag)
+ struct socket *so;
+ caddr_t arg;
+ int waitflag;
+{
+ register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg;
+ register struct mbuf *m;
+ struct mbuf *mp, *nam;
+ struct uio auio;
+ int flags, error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+#ifdef notdef
+ /*
+ * Define this to test for nfsds handling this under heavy load.
+ */
+ if (waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ; goto dorecs;
+ }
+#endif
+ auio.uio_procp = NULL;
+ if (so->so_type == SOCK_STREAM) {
+ /*
+ * If there are already records on the queue, defer soreceive()
+ * to an nfsd so that there is feedback to the TCP layer that
+ * the nfs servers are heavily loaded.
+ */
+ if (slp->ns_rec && waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ;
+ goto dorecs;
+ }
+
+ /*
+ * Do soreceive().
+ */
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp, (struct mbuf **)0, &flags);
+ if (error || mp == (struct mbuf *)0) {
+ if (error == EWOULDBLOCK)
+ slp->ns_flag |= SLP_NEEDQ;
+ else
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ m = mp;
+ if (slp->ns_rawend) {
+ slp->ns_rawend->m_next = m;
+ slp->ns_cc += 1000000000 - auio.uio_resid;
+ } else {
+ slp->ns_raw = m;
+ slp->ns_cc = 1000000000 - auio.uio_resid;
+ }
+ while (m->m_next)
+ m = m->m_next;
+ slp->ns_rawend = m;
+
+ /*
+ * Now try and parse record(s) out of the raw stream data.
+ */
+ if (error = nfsrv_getstream(slp, waitflag)) {
+ if (error == EPERM)
+ slp->ns_flag |= SLP_DISCONN;
+ else
+ slp->ns_flag |= SLP_NEEDQ;
+ }
+ } else {
+ do {
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp,
+ (struct mbuf **)0, &flags);
+ if (mp) {
+ nfs_realign(mp, 10 * NFSX_UNSIGNED);
+ if (nam) {
+ m = nam;
+ m->m_next = mp;
+ } else
+ m = mp;
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = m;
+ else
+ slp->ns_rec = m;
+ slp->ns_recend = m;
+ m->m_nextpkt = (struct mbuf *)0;
+ }
+ if (error) {
+ if ((so->so_proto->pr_flags & PR_CONNREQUIRED)
+ && error != EWOULDBLOCK) {
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ }
+ } while (mp);
+ }
+
+ /*
+ * Now try and process the request records, non-blocking.
+ */
+dorecs:
+ if (waitflag == M_DONTWAIT &&
+ (slp->ns_rec || (slp->ns_flag & (SLP_NEEDQ | SLP_DISCONN))))
+ nfsrv_wakenfsd(slp);
+}
+
+/*
+ * Try and extract an RPC request from the mbuf data list received on a
+ * stream socket. The "waitflag" argument indicates whether or not it
+ * can sleep.
+ */
+nfsrv_getstream(slp, waitflag)
+ register struct nfssvc_sock *slp;
+ int waitflag;
+{
+ register struct mbuf *m;
+ register char *cp1, *cp2;
+ register int len;
+ struct mbuf *om, *m2, *recm;
+ u_long recmark;
+
+ if (slp->ns_flag & SLP_GETSTREAM)
+ panic("nfs getstream");
+ slp->ns_flag |= SLP_GETSTREAM;
+ for (;;) {
+ if (slp->ns_reclen == 0) {
+ if (slp->ns_cc < NFSX_UNSIGNED) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ m = slp->ns_raw;
+ if (m->m_len >= NFSX_UNSIGNED) {
+ bcopy(mtod(m, caddr_t), (caddr_t)&recmark, NFSX_UNSIGNED);
+ m->m_data += NFSX_UNSIGNED;
+ m->m_len -= NFSX_UNSIGNED;
+ } else {
+ cp1 = (caddr_t)&recmark;
+ cp2 = mtod(m, caddr_t);
+ while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
+ while (m->m_len == 0) {
+ m = m->m_next;
+ cp2 = mtod(m, caddr_t);
+ }
+ *cp1++ = *cp2++;
+ m->m_data++;
+ m->m_len--;
+ }
+ }
+ slp->ns_cc -= NFSX_UNSIGNED;
+ slp->ns_reclen = ntohl(recmark) & ~0x80000000;
+ if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EPERM);
+ }
+ }
+
+ /*
+ * Now get the record part.
+ */
+ if (slp->ns_cc == slp->ns_reclen) {
+ recm = slp->ns_raw;
+ slp->ns_raw = slp->ns_rawend = (struct mbuf *)0;
+ slp->ns_cc = slp->ns_reclen = 0;
+ } else if (slp->ns_cc > slp->ns_reclen) {
+ len = 0;
+ m = slp->ns_raw;
+ om = (struct mbuf *)0;
+ while (len < slp->ns_reclen) {
+ if ((len + m->m_len) > slp->ns_reclen) {
+ m2 = m_copym(m, 0, slp->ns_reclen - len,
+ waitflag);
+ if (m2) {
+ if (om) {
+ om->m_next = m2;
+ recm = slp->ns_raw;
+ } else
+ recm = m2;
+ m->m_data += slp->ns_reclen - len;
+ m->m_len -= slp->ns_reclen - len;
+ len = slp->ns_reclen;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EWOULDBLOCK);
+ }
+ } else if ((len + m->m_len) == slp->ns_reclen) {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ recm = slp->ns_raw;
+ om->m_next = (struct mbuf *)0;
+ } else {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ }
+ }
+ slp->ns_raw = m;
+ slp->ns_cc -= len;
+ slp->ns_reclen = 0;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ nfs_realign(recm, 10 * NFSX_UNSIGNED);
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = recm;
+ else
+ slp->ns_rec = recm;
+ slp->ns_recend = recm;
+ }
+}
+
+/*
+ * Parse an RPC header.
+ */
+nfsrv_dorec(slp, nd)
+ register struct nfssvc_sock *slp;
+ register struct nfsd *nd;
+{
+ register struct mbuf *m;
+ int error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0 ||
+ (m = slp->ns_rec) == (struct mbuf *)0)
+ return (ENOBUFS);
+ if (slp->ns_rec = m->m_nextpkt)
+ m->m_nextpkt = (struct mbuf *)0;
+ else
+ slp->ns_recend = (struct mbuf *)0;
+ if (m->m_type == MT_SONAME) {
+ nd->nd_nam = m;
+ nd->nd_md = nd->nd_mrep = m->m_next;
+ m->m_next = (struct mbuf *)0;
+ } else {
+ nd->nd_nam = (struct mbuf *)0;
+ nd->nd_md = nd->nd_mrep = m;
+ }
+ nd->nd_dpos = mtod(nd->nd_md, caddr_t);
+ if (error = nfs_getreq(nd, TRUE)) {
+ m_freem(nd->nd_nam);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * Parse an RPC request
+ * - verify it
+ * - fill in the cred struct.
+ */
+nfs_getreq(nd, has_header)
+ register struct nfsd *nd;
+ int has_header;
+{
+ register int len, i;
+ register u_long *tl;
+ register long t1;
+ struct uio uio;
+ struct iovec iov;
+ caddr_t dpos, cp2;
+ u_long nfsvers, auth_type;
+ int error = 0, nqnfs = 0;
+ struct mbuf *mrep, *md;
+
+ mrep = nd->nd_mrep;
+ md = nd->nd_md;
+ dpos = nd->nd_dpos;
+ if (has_header) {
+ nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED);
+ nd->nd_retxid = *tl++;
+ if (*tl++ != rpc_call) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ } else {
+ nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED);
+ }
+ nd->nd_repstat = 0;
+ if (*tl++ != rpc_vers) {
+ nd->nd_repstat = ERPCMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nfsvers = nfs_vers;
+ if (*tl != nfs_prog) {
+ if (*tl == nqnfs_prog) {
+ nqnfs++;
+ nfsvers = nqnfs_vers;
+ } else {
+ nd->nd_repstat = EPROGUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ }
+ tl++;
+ if (*tl++ != nfsvers) {
+ nd->nd_repstat = EPROGMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nd->nd_procnum = fxdr_unsigned(u_long, *tl++);
+ if (nd->nd_procnum == NFSPROC_NULL)
+ return (0);
+ if (nd->nd_procnum >= NFS_NPROCS ||
+ (!nqnfs && nd->nd_procnum > NFSPROC_STATFS) ||
+ (*tl != rpc_auth_unix && *tl != rpc_auth_kerb)) {
+ nd->nd_repstat = EPROCUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ auth_type = *tl++;
+ len = fxdr_unsigned(int, *tl++);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+
+ /*
+ * Handle auth_unix or auth_kerb.
+ */
+ if (auth_type == rpc_auth_unix) {
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > NFS_MAXNAMLEN) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_adv(nfsm_rndup(len));
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_cr.cr_gid = fxdr_unsigned(gid_t, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len < 0 || len > RPCAUTH_UNIXGIDS) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED);
+ for (i = 1; i <= len; i++)
+ if (i < NGROUPS)
+ nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
+ else
+ tl++;
+ nd->nd_cr.cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1);
+ } else if (auth_type == rpc_auth_kerb) {
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_authlen = fxdr_unsigned(int, *tl);
+ iov.iov_len = uio.uio_resid = nfsm_rndup(nd->nd_authlen);
+ if (uio.uio_resid > (len - 2*NFSX_UNSIGNED)) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ uio.uio_offset = 0;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ iov.iov_base = (caddr_t)nd->nd_authstr;
+ nfsm_mtouio(&uio, uio.uio_resid);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ nd->nd_flag |= NFSD_NEEDAUTH;
+ }
+
+ /*
+ * Do we have any use for the verifier.
+ * According to the "Remote Procedure Call Protocol Spec." it
+ * should be AUTH_NULL, but some clients make it AUTH_UNIX?
+ * For now, just skip over it
+ */
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ if (len > 0) {
+ nfsm_adv(nfsm_rndup(len));
+ }
+
+ /*
+ * For nqnfs, get piggybacked lease request.
+ */
+ if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_nqlflag = fxdr_unsigned(int, *tl);
+ if (nd->nd_nqlflag) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_duration = fxdr_unsigned(int, *tl);
+ } else
+ nd->nd_duration = NQ_MINLEASE;
+ } else {
+ nd->nd_nqlflag = NQL_NOVAL;
+ nd->nd_duration = NQ_MINLEASE;
+ }
+ nd->nd_md = md;
+ nd->nd_dpos = dpos;
+ return (0);
+nfsmout:
+ return (error);
+}
+
+/*
+ * Search for a sleeping nfsd and wake it up.
+ * SIDE EFFECT: If none found, set NFSD_CHECKSLP flag, so that one of the
+ * running nfsds will go look for the work in the nfssvc_sock list.
+ */
+void
+nfsrv_wakenfsd(slp)
+ struct nfssvc_sock *slp;
+{
+ register struct nfsd *nd = nfsd_head.nd_next;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+ while (nd != (struct nfsd *)&nfsd_head) {
+ if (nd->nd_flag & NFSD_WAITING) {
+ nd->nd_flag &= ~NFSD_WAITING;
+ if (nd->nd_slp)
+ panic("nfsd wakeup");
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ wakeup((caddr_t)nd);
+ return;
+ }
+ nd = nd->nd_next;
+ }
+ slp->ns_flag |= SLP_DOREC;
+ nfsd_head.nd_flag |= NFSD_CHECKSLP;
+}
+
+nfs_msg(p, server, msg)
+ struct proc *p;
+ char *server, *msg;
+{
+ tpr_t tpr;
+
+ if (p)
+ tpr = tprintf_open(p);
+ else
+ tpr = NULL;
+ tprintf(tpr, "nfs server %s: %s\n", server, msg);
+ tprintf_close(tpr);