1108441Ssimokawa/*-
2108441Ssimokawa * Copyright (c) 1989, 1993
3108441Ssimokawa *	The Regents of the University of California.  All rights reserved.
4108441Ssimokawa *
5108441Ssimokawa * This code is derived from software contributed to Berkeley by
6108441Ssimokawa * Rick Macklem at The University of Guelph.
7108441Ssimokawa *
8108441Ssimokawa * Redistribution and use in source and binary forms, with or without
9108441Ssimokawa * modification, are permitted provided that the following conditions
10108441Ssimokawa * are met:
11108441Ssimokawa * 1. Redistributions of source code must retain the above copyright
12108441Ssimokawa *    notice, this list of conditions and the following disclaimer.
13108441Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright
14108441Ssimokawa *    notice, this list of conditions and the following disclaimer in the
15108441Ssimokawa *    documentation and/or other materials provided with the distribution.
16108441Ssimokawa * 4. Neither the name of the University nor the names of its contributors
17108441Ssimokawa *    may be used to endorse or promote products derived from this software
18108441Ssimokawa *    without specific prior written permission.
19108441Ssimokawa *
20108441Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21108441Ssimokawa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22108441Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23108441Ssimokawa * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24108441Ssimokawa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25108441Ssimokawa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26108441Ssimokawa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27108441Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28108441Ssimokawa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29108441Ssimokawa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30108441Ssimokawa * SUCH DAMAGE.
31108441Ssimokawa *
32108441Ssimokawa *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
33108441Ssimokawa */
34108441Ssimokawa
35185996Ssbruno#include <sys/cdefs.h>
36146442Scharnier__FBSDID("$FreeBSD$");
37146442Scharnier
38185996Ssbruno/*
39146442Scharnier * These functions support the macros and help fiddle mbuf chains for
40108441Ssimokawa * the nfs op functions.  They do things like create the rpc header and
41108441Ssimokawa * copy data between mbuf chains and uio lists.
42146442Scharnier */
43146442Scharnier
44108441Ssimokawa#include <sys/param.h>
45108441Ssimokawa#include <sys/systm.h>
46108441Ssimokawa#include <sys/kernel.h>
47185996Ssbruno#include <sys/bio.h>
48129760Sbrooks#include <sys/buf.h>
49108441Ssimokawa#include <sys/proc.h>
50108441Ssimokawa#include <sys/mount.h>
51120432Ssimokawa#include <sys/vnode.h>
52163712Simp#include <sys/namei.h>
53185996Ssbruno#include <sys/mbuf.h>
54185996Ssbruno#include <sys/socket.h>
55185996Ssbruno#include <sys/stat.h>
56185996Ssbruno#include <sys/malloc.h>
57185996Ssbruno#include <sys/module.h>
58185996Ssbruno#include <sys/sysent.h>
59185996Ssbruno#include <sys/syscall.h>
60185996Ssbruno#include <sys/sysctl.h>
61185996Ssbruno
62108441Ssimokawa#include <vm/vm.h>
63185996Ssbruno#include <vm/vm_object.h>
64108441Ssimokawa#include <vm/vm_extern.h>
65108441Ssimokawa
66108441Ssimokawa#include <nfs/nfsproto.h>
67108441Ssimokawa#include <nfsserver/nfs.h>
68108441Ssimokawa#include <nfs/xdr_subs.h>
69108441Ssimokawa#include <nfs/nfs_common.h>
70163712Simp
71108441Ssimokawa#include <netinet/in.h>
72182911Ssbruno
73182911Ssbrunoenum vtype nv3tov_type[8]= {
74163712Simp	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
75108441Ssimokawa};
76163712Simpnfstype nfsv3_type[9] = {
77109737Ssimokawa	NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON
78108657Ssimokawa};
79108441Ssimokawa
80108441Ssimokawastatic void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos,
81109787Ssimokawa    int how);
82185996Ssbruno
83182911SsbrunoSYSCTL_NODE(_vfs, OID_AUTO, nfs_common, CTLFLAG_RD, 0, "NFS common support");
84182911Ssbruno
85118457Ssimokawastatic int nfs_realign_test;
86182911SsbrunoSYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_test, CTLFLAG_RD,
87114217Ssimokawa    &nfs_realign_test, 0, "Number of realign tests done");
88114217Ssimokawa
89182911Ssbrunostatic int nfs_realign_count;
90114217SsimokawaSYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_count, CTLFLAG_RD,
91182911Ssbruno    &nfs_realign_count, 0, "Number of mbuf realignments done");
92182911Ssbruno
93114217Ssimokawa/*
94185996Ssbruno * copies mbuf chain to the uio scatter/gather list
95185996Ssbruno */
96182911Ssbrunoint
97182911Ssbrunonfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos)
98163712Simp{
99137028Ssimokawa	char *mbufcp, *uiocp;
100185996Ssbruno	int xfer, left, len;
101185996Ssbruno	struct mbuf *mp;
102185996Ssbruno	long uiosiz, rem;
103108441Ssimokawa	int error = 0;
104108441Ssimokawa
105129760Sbrooks	mp = *mrep;
106129760Sbrooks	mbufcp = *dpos;
107129760Sbrooks	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
108129760Sbrooks	rem = nfsm_rndup(siz)-siz;
109129760Sbrooks	while (siz > 0) {
110129760Sbrooks		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
111129760Sbrooks			return (EFBIG);
112182911Ssbruno		left = uiop->uio_iov->iov_len;
113182911Ssbruno		uiocp = uiop->uio_iov->iov_base;
114108441Ssimokawa		if (left > siz)
115109814Ssimokawa			left = siz;
116182911Ssbruno		uiosiz = left;
117108441Ssimokawa		while (left > 0) {
118182911Ssbruno			while (len == 0) {
119108441Ssimokawa				mp = mp->m_next;
120108441Ssimokawa				if (mp == NULL)
121108441Ssimokawa					return (EBADRPC);
122129760Sbrooks				mbufcp = mtod(mp, caddr_t);
123129760Sbrooks				len = mp->m_len;
124129760Sbrooks			}
125129760Sbrooks			xfer = (left > len) ? len : left;
126129760Sbrooks#ifdef notdef
127129760Sbrooks			/* Not Yet.. */
128129760Sbrooks			if (uiop->uio_iov->iov_op != NULL)
129129760Sbrooks				(*(uiop->uio_iov->iov_op))
130129760Sbrooks				(mbufcp, uiocp, xfer);
131129760Sbrooks			else
132129760Sbrooks#endif
133129760Sbrooks			if (uiop->uio_segflg == UIO_SYSSPACE)
134129760Sbrooks				bcopy(mbufcp, uiocp, xfer);
135129760Sbrooks			else
136129760Sbrooks				copyout(mbufcp, uiocp, xfer);
137129760Sbrooks			left -= xfer;
138129760Sbrooks			len -= xfer;
139129760Sbrooks			mbufcp += xfer;
140129760Sbrooks			uiocp += xfer;
141129760Sbrooks			uiop->uio_offset += xfer;
142129760Sbrooks			uiop->uio_resid -= xfer;
143129760Sbrooks		}
144129760Sbrooks		if (uiop->uio_iov->iov_len <= siz) {
145129760Sbrooks			uiop->uio_iovcnt--;
146182911Ssbruno			uiop->uio_iov++;
147182911Ssbruno		} else {
148182911Ssbruno			uiop->uio_iov->iov_base =
149182911Ssbruno			    (char *)uiop->uio_iov->iov_base + uiosiz;
150129760Sbrooks			uiop->uio_iov->iov_len -= uiosiz;
151129760Sbrooks		}
152129760Sbrooks		siz -= uiosiz;
153129760Sbrooks	}
154129760Sbrooks	*dpos = mbufcp;
155228790Seadler	*mrep = mp;
156129760Sbrooks	if (rem > 0) {
157129760Sbrooks		if (len < rem)
158129760Sbrooks			error = nfs_adv(mrep, dpos, rem, len);
159182911Ssbruno		else
160182911Ssbruno			*dpos += rem;
161182911Ssbruno	}
162129760Sbrooks	return (error);
163182911Ssbruno}
164129760Sbrooks
165129760Sbrooks/*
166129760Sbrooks * Help break down an mbuf chain by setting the first siz bytes contiguous
167129760Sbrooks * pointed to by returned val.
168129760Sbrooks * This is used by the macros nfsm_dissect for tough
169129760Sbrooks * cases. (The macros use the vars. dpos and dpos2)
170129760Sbrooks */
171129760Sbrooksvoid *
172108657Ssimokawanfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how)
173108441Ssimokawa{
174108441Ssimokawa	struct mbuf *mp, *mp2;
175109814Ssimokawa	int siz2, xfer;
176109814Ssimokawa	caddr_t ptr, npos = NULL;
177129760Sbrooks	void *ret;
178176810Ssimokawa
179108441Ssimokawa	mp = *mdp;
180108441Ssimokawa	while (left == 0) {
181182911Ssbruno		*mdp = mp = mp->m_next;
182182911Ssbruno		if (mp == NULL)
183182911Ssbruno			return (NULL);
184182911Ssbruno		left = mp->m_len;
185109814Ssimokawa		*dposp = mtod(mp, caddr_t);
186176810Ssimokawa	}
187109814Ssimokawa	if (left >= siz) {
188109814Ssimokawa		ret = *dposp;
189129760Sbrooks		*dposp += siz;
190129760Sbrooks	} else if (mp->m_next == NULL) {
191176810Ssimokawa		return (NULL);
192176810Ssimokawa	} else if (siz > MHLEN) {
193176810Ssimokawa		panic("nfs S too big");
194110578Ssimokawa	} else {
195129760Sbrooks		mp2 = m_get(how, MT_DATA);
196176810Ssimokawa		if (mp2 == NULL)
197176810Ssimokawa			return (NULL);
198108441Ssimokawa		mp2->m_len = siz;
199108441Ssimokawa		mp2->m_next = mp->m_next;
200109814Ssimokawa		mp->m_next = mp2;
201108441Ssimokawa		mp->m_len -= left;
202108441Ssimokawa		mp = mp2;
203108657Ssimokawa		ptr = mtod(mp, caddr_t);
204146442Scharnier		ret = ptr;
205108441Ssimokawa		bcopy(*dposp, ptr, left);		/* Copy what was left */
206108441Ssimokawa		siz2 = siz-left;
207108441Ssimokawa		ptr += left;
208108441Ssimokawa		mp2 = mp->m_next;
209108441Ssimokawa		npos = mtod(mp2, caddr_t);
210182911Ssbruno		/* Loop around copying up the siz2 bytes */
211182911Ssbruno		while (siz2 > 0) {
212108441Ssimokawa			if (mp2 == NULL)
213114217Ssimokawa				return (NULL);
214114217Ssimokawa			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
215114217Ssimokawa			if (xfer > 0) {
216114217Ssimokawa				bcopy(mtod(mp2, caddr_t), ptr, xfer);
217108441Ssimokawa				mp2->m_data += xfer;
218108441Ssimokawa				mp2->m_len -= xfer;
219108441Ssimokawa				ptr += xfer;
220108441Ssimokawa				siz2 -= xfer;
221146442Scharnier			}
222108441Ssimokawa			if (siz2 > 0) {
223108441Ssimokawa				mp2 = mp2->m_next;
224108441Ssimokawa				if (mp2 != NULL)
225108441Ssimokawa					npos = mtod(mp2, caddr_t);
226113584Ssimokawa			}
227113584Ssimokawa		}
228108441Ssimokawa		*mdp = mp2;
229108441Ssimokawa		*dposp = mtod(mp2, caddr_t);
230146442Scharnier		if (!nfsm_aligned(*dposp, u_int32_t)) {
231176810Ssimokawa			bcopy(*dposp, npos, mp2->m_len);
232108441Ssimokawa			mp2->m_data = npos;
233108441Ssimokawa			*dposp = npos;
234182911Ssbruno		}
235108441Ssimokawa	}
236108441Ssimokawa	return (ret);
237108441Ssimokawa}
238146442Scharnier
239108441Ssimokawa/*
240108441Ssimokawa * Advance the position in the mbuf chain.
241108441Ssimokawa */
242108441Ssimokawaint
243108657Ssimokawanfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left)
244182911Ssbruno{
245182911Ssbruno	struct mbuf *m;
246182911Ssbruno	int s;
247182911Ssbruno
248182911Ssbruno	m = *mdp;
249182911Ssbruno	s = left;
250182911Ssbruno	while (s < offs) {
251182911Ssbruno		offs -= s;
252182911Ssbruno		m = m->m_next;
253182911Ssbruno		if (m == NULL)
254182911Ssbruno			return (EBADRPC);
255182911Ssbruno		s = m->m_len;
256182911Ssbruno	}
257182911Ssbruno	*mdp = m;
258182911Ssbruno	*dposp = mtod(m, caddr_t)+offs;
259108657Ssimokawa	return (0);
260108441Ssimokawa}
261108441Ssimokawa
262108441Ssimokawavoid *
263108441Ssimokawanfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos)
264108441Ssimokawa{
265182911Ssbruno	struct mbuf *mb2;
266182911Ssbruno	void *ret;
267108441Ssimokawa
268108441Ssimokawa	if (s > M_TRAILINGSPACE(*mb)) {
269108441Ssimokawa		mb2 = m_get(M_WAITOK, MT_DATA);
270108441Ssimokawa		if (s > MLEN)
271108441Ssimokawa			panic("build > MLEN");
272108441Ssimokawa		(*mb)->m_next = mb2;
273182911Ssbruno		*mb = mb2;
274108441Ssimokawa		(*mb)->m_len = 0;
275182911Ssbruno		*bpos = mtod(*mb, caddr_t);
276108441Ssimokawa	}
277108441Ssimokawa	ret = *bpos;
278108441Ssimokawa	(*mb)->m_len += s;
279108441Ssimokawa	*bpos += s;
280108441Ssimokawa	return (ret);
281114274Ssimokawa}
282182911Ssbruno
283114274Ssimokawavoid *
284108441Ssimokawanfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos)
285108441Ssimokawa{
286108657Ssimokawa
287182911Ssbruno	return (nfsm_dissect_xx_sub(s, md, dpos, M_WAITOK));
288114217Ssimokawa}
289114217Ssimokawa
290114217Ssimokawavoid *
291114217Ssimokawanfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos)
292182911Ssbruno{
293182911Ssbruno
294114217Ssimokawa	return (nfsm_dissect_xx_sub(s, md, dpos, M_NOWAIT));
295114217Ssimokawa}
296114217Ssimokawa
297114217Ssimokawastatic void *
298114217Ssimokawanfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how)
299114217Ssimokawa{
300114274Ssimokawa	int t1;
301182911Ssbruno	char *cp2;
302114274Ssimokawa	void *ret;
303114217Ssimokawa
304114217Ssimokawa	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
305114217Ssimokawa	if (t1 >= s) {
306114217Ssimokawa		ret = *dpos;
307114217Ssimokawa		*dpos += s;
308114217Ssimokawa		return (ret);
309114217Ssimokawa	}
310114217Ssimokawa	cp2 = nfsm_disct(md, dpos, s, t1, how);
311182911Ssbruno	return (cp2);
312182911Ssbruno}
313114217Ssimokawa
314114217Ssimokawaint
315114217Ssimokawanfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos)
316114217Ssimokawa{
317114217Ssimokawa	u_int32_t *tl;
318114217Ssimokawa
319114217Ssimokawa	tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos);
320114217Ssimokawa	if (tl == NULL)
321114217Ssimokawa		return (EBADRPC);
322114217Ssimokawa	*s = fxdr_unsigned(int32_t, *tl);
323114217Ssimokawa	if (*s > m)
324114274Ssimokawa		return (EBADRPC);
325182911Ssbruno	return (0);
326114274Ssimokawa}
327114217Ssimokawa
328114217Ssimokawaint
329114217Ssimokawanfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos)
330163712Simp{
331108441Ssimokawa	int t1;
332109814Ssimokawa
333109814Ssimokawa	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
334129760Sbrooks	if (t1 >= s) {
335129760Sbrooks		*dpos += s;
336108441Ssimokawa		return (0);
337108441Ssimokawa	}
338108441Ssimokawa	t1 = nfs_adv(md, dpos, s, t1);
339182911Ssbruno	if (t1)
340182911Ssbruno		return (t1);
341182911Ssbruno	return (0);
342182911Ssbruno}
343108441Ssimokawa
344109814Ssimokawa/*
345109814Ssimokawa * Check for badly aligned mbuf data and realign by copying the unaligned
346109814Ssimokawa * portion of the data into a new mbuf chain and freeing the portions of the
347108441Ssimokawa * old chain that were replaced.
348109814Ssimokawa *
349129760Sbrooks * We cannot simply realign the data within the existing mbuf chain because
350129760Sbrooks * the underlying buffers may contain other rpc commands and we cannot afford
351129760Sbrooks * to overwrite them.
352129760Sbrooks *
353163712Simp * We would prefer to avoid this situation entirely.  The situation does not
354108441Ssimokawa * occur with NFS/UDP and is supposed to only occassionally occur with TCP.
355108441Ssimokawa * Use vfs.nfs_common.realign_count and realign_test to check this.
356108441Ssimokawa */
357108441Ssimokawaint
358108441Ssimokawanfs_realign(struct mbuf **pm, int how)
359109814Ssimokawa{
360108441Ssimokawa	struct mbuf *m, *n;
361108441Ssimokawa	int off;
362108441Ssimokawa
363108441Ssimokawa	++nfs_realign_test;
364109814Ssimokawa	while ((m = *pm) != NULL) {
365108441Ssimokawa		if (!nfsm_aligned(m->m_len, u_int32_t) ||
366108441Ssimokawa		    !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) {
367108657Ssimokawa			/*
368163712Simp			 * NB: we can't depend on m_pkthdr.len to help us
369108441Ssimokawa			 * decide what to do here.  May not be worth doing
370129760Sbrooks			 * the m_length calculation as m_copyback will
371116141Ssimokawa			 * expand the mbuf chain below as needed.
372129760Sbrooks			 */
373108441Ssimokawa			if (m_length(m, NULL) >= MINCLSIZE) {
374116141Ssimokawa				/* NB: m_copyback handles space > MCLBYTES */
375129760Sbrooks				n = m_getcl(how, MT_DATA, 0);
376129760Sbrooks			} else
377116141Ssimokawa				n = m_get(how, MT_DATA);
378116141Ssimokawa			if (n == NULL)
379116141Ssimokawa				return (ENOMEM);
380116141Ssimokawa			/*
381129760Sbrooks			 * Align the remainder of the mbuf chain.
382116141Ssimokawa			 */
383116141Ssimokawa			n->m_len = 0;
384116141Ssimokawa			off = 0;
385116141Ssimokawa			while (m != NULL) {
386129760Sbrooks				m_copyback(n, off, m->m_len, mtod(m, caddr_t));
387108441Ssimokawa				off += m->m_len;
388108441Ssimokawa				m = m->m_next;
389108657Ssimokawa			}
390108441Ssimokawa			m_freem(*pm);
391108441Ssimokawa			*pm = n;
392108441Ssimokawa			++nfs_realign_count;
393109991Ssimokawa			break;
394109814Ssimokawa		}
395108441Ssimokawa		pm = &m->m_next;
396182911Ssbruno	}
397182911Ssbruno	return (0);
398182911Ssbruno}
399182911Ssbruno
400108441Ssimokawastatic moduledata_t nfs_common_mod = {
401109814Ssimokawa	"nfs_common",
402109814Ssimokawa	NULL,
403108441Ssimokawa	NULL
404108441Ssimokawa};
405109814Ssimokawa
406109814SsimokawaDECLARE_MODULE(nfs_common, nfs_common_mod, SI_SUB_VFS, SI_ORDER_ANY);
407109814SsimokawaMODULE_VERSION(nfs_common, 1);
408109814Ssimokawa