1139823Simp/*-
21541Srgrimes * Copyright (c) 1989, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software contributed to Berkeley by
61541Srgrimes * Rick Macklem at The University of Guelph.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 4. Neither the name of the University nor the names of its contributors
171541Srgrimes *    may be used to endorse or promote products derived from this software
181541Srgrimes *    without specific prior written permission.
191541Srgrimes *
201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301541Srgrimes * SUCH DAMAGE.
311541Srgrimes *
3236503Speter *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
331541Srgrimes */
341541Srgrimes
3583651Speter#include <sys/cdefs.h>
3683651Speter__FBSDID("$FreeBSD$");
3783651Speter
381541Srgrimes/*
391541Srgrimes * These functions support the macros and help fiddle mbuf chains for
40203731Smarius * the nfs op functions.  They do things like create the rpc header and
411541Srgrimes * copy data between mbuf chains and uio lists.
421541Srgrimes */
4383651Speter
441541Srgrimes#include <sys/param.h>
4548274Speter#include <sys/systm.h>
4648274Speter#include <sys/kernel.h>
4760041Sphk#include <sys/bio.h>
4831886Sbde#include <sys/buf.h>
491541Srgrimes#include <sys/proc.h>
501541Srgrimes#include <sys/mount.h>
511541Srgrimes#include <sys/vnode.h>
521541Srgrimes#include <sys/namei.h>
531541Srgrimes#include <sys/mbuf.h>
541541Srgrimes#include <sys/socket.h>
551541Srgrimes#include <sys/stat.h>
569336Sdfr#include <sys/malloc.h>
57203968Smarius#include <sys/module.h>
582997Swollman#include <sys/sysent.h>
592997Swollman#include <sys/syscall.h>
60203732Smarius#include <sys/sysctl.h>
611541Srgrimes
623305Sphk#include <vm/vm.h>
6312662Sdg#include <vm/vm_object.h>
6412662Sdg#include <vm/vm_extern.h>
653305Sphk
669336Sdfr#include <nfs/nfsproto.h>
6783651Speter#include <nfsserver/nfs.h>
681541Srgrimes#include <nfs/xdr_subs.h>
6983651Speter#include <nfs/nfs_common.h>
701541Srgrimes
711541Srgrimes#include <netinet/in.h>
721541Srgrimes
7312911Sphkenum vtype nv3tov_type[8]= {
7412911Sphk	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
7512911Sphk};
7683651Speternfstype nfsv3_type[9] = {
7783651Speter	NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON
789336Sdfr};
799336Sdfr
80203731Smariusstatic void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos,
81203731Smarius    int how);
82138463Sps
83203968SmariusSYSCTL_NODE(_vfs, OID_AUTO, nfs_common, CTLFLAG_RD, 0, "NFS common support");
84203732Smarius
85203732Smariusstatic int nfs_realign_test;
86203968SmariusSYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_test, CTLFLAG_RD,
87203968Smarius    &nfs_realign_test, 0, "Number of realign tests done");
88203732Smarius
89203732Smariusstatic int nfs_realign_count;
90203968SmariusSYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_count, CTLFLAG_RD,
91203968Smarius    &nfs_realign_count, 0, "Number of mbuf realignments done");
92203732Smarius
931541Srgrimes/*
941541Srgrimes * copies mbuf chain to the uio scatter/gather list
951541Srgrimes */
961549Srgrimesint
9783651Speternfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos)
981541Srgrimes{
9983651Speter	char *mbufcp, *uiocp;
10083651Speter	int xfer, left, len;
10183651Speter	struct mbuf *mp;
1021541Srgrimes	long uiosiz, rem;
1031541Srgrimes	int error = 0;
1041541Srgrimes
1051541Srgrimes	mp = *mrep;
1061541Srgrimes	mbufcp = *dpos;
1071541Srgrimes	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
1081541Srgrimes	rem = nfsm_rndup(siz)-siz;
1091541Srgrimes	while (siz > 0) {
1101541Srgrimes		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
1111541Srgrimes			return (EFBIG);
1121541Srgrimes		left = uiop->uio_iov->iov_len;
1131541Srgrimes		uiocp = uiop->uio_iov->iov_base;
1141541Srgrimes		if (left > siz)
1151541Srgrimes			left = siz;
1161541Srgrimes		uiosiz = left;
1171541Srgrimes		while (left > 0) {
1181541Srgrimes			while (len == 0) {
1191541Srgrimes				mp = mp->m_next;
1201541Srgrimes				if (mp == NULL)
1211541Srgrimes					return (EBADRPC);
1221541Srgrimes				mbufcp = mtod(mp, caddr_t);
1231541Srgrimes				len = mp->m_len;
1241541Srgrimes			}
1251541Srgrimes			xfer = (left > len) ? len : left;
1261541Srgrimes#ifdef notdef
1271541Srgrimes			/* Not Yet.. */
1281541Srgrimes			if (uiop->uio_iov->iov_op != NULL)
1291541Srgrimes				(*(uiop->uio_iov->iov_op))
1301541Srgrimes				(mbufcp, uiocp, xfer);
1311541Srgrimes			else
1321541Srgrimes#endif
133195631Smarcel			if (uiop->uio_segflg == UIO_SYSSPACE)
1341541Srgrimes				bcopy(mbufcp, uiocp, xfer);
135195631Smarcel			else
1361541Srgrimes				copyout(mbufcp, uiocp, xfer);
1371541Srgrimes			left -= xfer;
1381541Srgrimes			len -= xfer;
1391541Srgrimes			mbufcp += xfer;
1401541Srgrimes			uiocp += xfer;
1411541Srgrimes			uiop->uio_offset += xfer;
1421541Srgrimes			uiop->uio_resid -= xfer;
1431541Srgrimes		}
1441541Srgrimes		if (uiop->uio_iov->iov_len <= siz) {
1451541Srgrimes			uiop->uio_iovcnt--;
1461541Srgrimes			uiop->uio_iov++;
1471541Srgrimes		} else {
148104908Smike			uiop->uio_iov->iov_base =
149104908Smike			    (char *)uiop->uio_iov->iov_base + uiosiz;
1501541Srgrimes			uiop->uio_iov->iov_len -= uiosiz;
1511541Srgrimes		}
1521541Srgrimes		siz -= uiosiz;
1531541Srgrimes	}
1541541Srgrimes	*dpos = mbufcp;
1551541Srgrimes	*mrep = mp;
1561541Srgrimes	if (rem > 0) {
1571541Srgrimes		if (len < rem)
1581541Srgrimes			error = nfs_adv(mrep, dpos, rem, len);
1591541Srgrimes		else
1601541Srgrimes			*dpos += rem;
1611541Srgrimes	}
1621541Srgrimes	return (error);
1631541Srgrimes}
1641541Srgrimes
1651541Srgrimes/*
1661541Srgrimes * Help break down an mbuf chain by setting the first siz bytes contiguous
1671541Srgrimes * pointed to by returned val.
16884057Speter * This is used by the macros nfsm_dissect for tough
1691541Srgrimes * cases. (The macros use the vars. dpos and dpos2)
1701541Srgrimes */
17184057Spetervoid *
172138463Spsnfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how)
1731541Srgrimes{
17483651Speter	struct mbuf *mp, *mp2;
17583651Speter	int siz2, xfer;
176148008Sps	caddr_t ptr, npos = NULL;
17784057Speter	void *ret;
1781541Srgrimes
1791541Srgrimes	mp = *mdp;
1801541Srgrimes	while (left == 0) {
1811541Srgrimes		*mdp = mp = mp->m_next;
1821541Srgrimes		if (mp == NULL)
183203731Smarius			return (NULL);
1841541Srgrimes		left = mp->m_len;
1851541Srgrimes		*dposp = mtod(mp, caddr_t);
1861541Srgrimes	}
1871541Srgrimes	if (left >= siz) {
18884057Speter		ret = *dposp;
1891541Srgrimes		*dposp += siz;
1901541Srgrimes	} else if (mp->m_next == NULL) {
191203731Smarius		return (NULL);
1921541Srgrimes	} else if (siz > MHLEN) {
1931541Srgrimes		panic("nfs S too big");
1941541Srgrimes	} else {
195248318Sglebius		mp2 = m_get(how, MT_DATA);
196138463Sps		if (mp2 == NULL)
197203731Smarius			return (NULL);
198148008Sps		mp2->m_len = siz;
1991541Srgrimes		mp2->m_next = mp->m_next;
2001541Srgrimes		mp->m_next = mp2;
2011541Srgrimes		mp->m_len -= left;
2021541Srgrimes		mp = mp2;
20384057Speter		ptr = mtod(mp, caddr_t);
20484057Speter		ret = ptr;
20583366Sjulian		bcopy(*dposp, ptr, left);		/* Copy what was left */
2061541Srgrimes		siz2 = siz-left;
20783366Sjulian		ptr += left;
2081541Srgrimes		mp2 = mp->m_next;
209148008Sps		npos = mtod(mp2, caddr_t);
2101541Srgrimes		/* Loop around copying up the siz2 bytes */
2111541Srgrimes		while (siz2 > 0) {
2121541Srgrimes			if (mp2 == NULL)
213203731Smarius				return (NULL);
2141541Srgrimes			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
2151541Srgrimes			if (xfer > 0) {
21683366Sjulian				bcopy(mtod(mp2, caddr_t), ptr, xfer);
21784079Speter				mp2->m_data += xfer;
2181541Srgrimes				mp2->m_len -= xfer;
21983366Sjulian				ptr += xfer;
2201541Srgrimes				siz2 -= xfer;
2211541Srgrimes			}
222148008Sps			if (siz2 > 0) {
2231541Srgrimes				mp2 = mp2->m_next;
224148008Sps				if (mp2 != NULL)
225148008Sps					npos = mtod(mp2, caddr_t);
226148008Sps			}
2271541Srgrimes		}
2281541Srgrimes		*mdp = mp2;
2291541Srgrimes		*dposp = mtod(mp2, caddr_t);
230148008Sps		if (!nfsm_aligned(*dposp, u_int32_t)) {
231148008Sps			bcopy(*dposp, npos, mp2->m_len);
232148008Sps			mp2->m_data = npos;
233148008Sps			*dposp = npos;
234148008Sps		}
2351541Srgrimes	}
236203731Smarius	return (ret);
2371541Srgrimes}
2381541Srgrimes
2391541Srgrimes/*
2401541Srgrimes * Advance the position in the mbuf chain.
2411541Srgrimes */
2421549Srgrimesint
24383651Speternfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left)
2441541Srgrimes{
24583651Speter	struct mbuf *m;
24683651Speter	int s;
2471541Srgrimes
2481541Srgrimes	m = *mdp;
2491541Srgrimes	s = left;
2501541Srgrimes	while (s < offs) {
2511541Srgrimes		offs -= s;
2521541Srgrimes		m = m->m_next;
2531541Srgrimes		if (m == NULL)
2541541Srgrimes			return (EBADRPC);
2551541Srgrimes		s = m->m_len;
2561541Srgrimes	}
2571541Srgrimes	*mdp = m;
2581541Srgrimes	*dposp = mtod(m, caddr_t)+offs;
2591541Srgrimes	return (0);
2601541Srgrimes}
2611541Srgrimes
26284002Spetervoid *
26384002Speternfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos)
2641541Srgrimes{
26583651Speter	struct mbuf *mb2;
26684002Speter	void *ret;
2671541Srgrimes
26883651Speter	if (s > M_TRAILINGSPACE(*mb)) {
269248318Sglebius		mb2 = m_get(M_WAITOK, MT_DATA);
27083651Speter		if (s > MLEN)
27183651Speter			panic("build > MLEN");
27283651Speter		(*mb)->m_next = mb2;
27383651Speter		*mb = mb2;
27483651Speter		(*mb)->m_len = 0;
27583651Speter		*bpos = mtod(*mb, caddr_t);
2761541Srgrimes	}
27784002Speter	ret = *bpos;
27883651Speter	(*mb)->m_len += s;
27983651Speter	*bpos += s;
280203731Smarius	return (ret);
2811541Srgrimes}
2821541Srgrimes
28384057Spetervoid *
28484057Speternfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos)
2851541Srgrimes{
286203731Smarius
287243882Sglebius	return (nfsm_dissect_xx_sub(s, md, dpos, M_WAITOK));
288138463Sps}
289138463Sps
290138463Spsvoid *
291138463Spsnfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos)
292138463Sps{
293203731Smarius
294243882Sglebius	return (nfsm_dissect_xx_sub(s, md, dpos, M_NOWAIT));
295138463Sps}
296138463Sps
297138463Spsstatic void *
298138463Spsnfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how)
299138463Sps{
30083651Speter	int t1;
30183651Speter	char *cp2;
30284057Speter	void *ret;
3031541Srgrimes
30483651Speter	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
30583651Speter	if (t1 >= s) {
30684057Speter		ret = *dpos;
30783651Speter		*dpos += s;
308203731Smarius		return (ret);
30927446Sdfr	}
310203731Smarius	cp2 = nfsm_disct(md, dpos, s, t1, how);
311203731Smarius	return (cp2);
3121541Srgrimes}
3131541Srgrimes
31427446Sdfrint
31588091Siedowsenfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos)
31627446Sdfr{
31788091Siedowse	u_int32_t *tl;
31827446Sdfr
31988091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos);
32088091Siedowse	if (tl == NULL)
321203731Smarius		return (EBADRPC);
32288091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
32383651Speter	if (*s > m)
324203731Smarius		return (EBADRPC);
325203731Smarius	return (0);
32627446Sdfr}
3271541Srgrimes
3285455Sdgint
32988091Siedowsenfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos)
3309336Sdfr{
33183651Speter	int t1;
3329336Sdfr
33383651Speter	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
33488731Siedowse	if (t1 >= s) {
33583651Speter		*dpos += s;
336203731Smarius		return (0);
3379336Sdfr	}
33883651Speter	t1 = nfs_adv(md, dpos, s, t1);
33983651Speter	if (t1)
340203731Smarius		return (t1);
341203731Smarius	return (0);
3429336Sdfr}
343203732Smarius
344203732Smarius/*
345203732Smarius * Check for badly aligned mbuf data and realign by copying the unaligned
346203732Smarius * portion of the data into a new mbuf chain and freeing the portions of the
347203732Smarius * old chain that were replaced.
348203732Smarius *
349203732Smarius * We cannot simply realign the data within the existing mbuf chain because
350203732Smarius * the underlying buffers may contain other rpc commands and we cannot afford
351203732Smarius * to overwrite them.
352203732Smarius *
353203732Smarius * We would prefer to avoid this situation entirely.  The situation does not
354203732Smarius * occur with NFS/UDP and is supposed to only occassionally occur with TCP.
355221973Srmacklem * Use vfs.nfs_common.realign_count and realign_test to check this.
356203732Smarius */
357203732Smariusint
358203732Smariusnfs_realign(struct mbuf **pm, int how)
359203732Smarius{
360203732Smarius	struct mbuf *m, *n;
361203732Smarius	int off;
362203732Smarius
363203732Smarius	++nfs_realign_test;
364203732Smarius	while ((m = *pm) != NULL) {
365203732Smarius		if (!nfsm_aligned(m->m_len, u_int32_t) ||
366203732Smarius		    !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) {
367203732Smarius			/*
368203732Smarius			 * NB: we can't depend on m_pkthdr.len to help us
369203732Smarius			 * decide what to do here.  May not be worth doing
370203732Smarius			 * the m_length calculation as m_copyback will
371203732Smarius			 * expand the mbuf chain below as needed.
372203732Smarius			 */
373203732Smarius			if (m_length(m, NULL) >= MINCLSIZE) {
374203732Smarius				/* NB: m_copyback handles space > MCLBYTES */
375203732Smarius				n = m_getcl(how, MT_DATA, 0);
376203732Smarius			} else
377203732Smarius				n = m_get(how, MT_DATA);
378203732Smarius			if (n == NULL)
379203732Smarius				return (ENOMEM);
380203732Smarius			/*
381203732Smarius			 * Align the remainder of the mbuf chain.
382203732Smarius			 */
383203732Smarius			n->m_len = 0;
384203732Smarius			off = 0;
385203732Smarius			while (m != NULL) {
386203732Smarius				m_copyback(n, off, m->m_len, mtod(m, caddr_t));
387203732Smarius				off += m->m_len;
388203732Smarius				m = m->m_next;
389203732Smarius			}
390203732Smarius			m_freem(*pm);
391203732Smarius			*pm = n;
392203732Smarius			++nfs_realign_count;
393203732Smarius			break;
394203732Smarius		}
395203732Smarius		pm = &m->m_next;
396203732Smarius	}
397203732Smarius	return (0);
398203732Smarius}
399203968Smarius
400203968Smariusstatic moduledata_t nfs_common_mod = {
401203968Smarius	"nfs_common",
402203968Smarius	NULL,
403203968Smarius	NULL
404203968Smarius};
405203968Smarius
406203968SmariusDECLARE_MODULE(nfs_common, nfs_common_mod, SI_SUB_VFS, SI_ORDER_ANY);
407203968SmariusMODULE_VERSION(nfs_common, 1);
408