1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38/*
39 * These functions support the macros and help fiddle mbuf chains for
40 * the nfs op functions.  They do things like create the rpc header and
41 * copy data between mbuf chains and uio lists.
42 */
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/kernel.h>
47#include <sys/bio.h>
48#include <sys/buf.h>
49#include <sys/proc.h>
50#include <sys/mount.h>
51#include <sys/vnode.h>
52#include <sys/namei.h>
53#include <sys/mbuf.h>
54#include <sys/socket.h>
55#include <sys/stat.h>
56#include <sys/malloc.h>
57#include <sys/module.h>
58#include <sys/sysent.h>
59#include <sys/syscall.h>
60#include <sys/sysctl.h>
61
62#include <vm/vm.h>
63#include <vm/vm_object.h>
64#include <vm/vm_extern.h>
65
66#include <nfs/nfsproto.h>
67#include <nfsserver/nfs.h>
68#include <nfs/xdr_subs.h>
69#include <nfs/nfs_common.h>
70
71#include <netinet/in.h>
72
73enum vtype nv3tov_type[8]= {
74	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
75};
76nfstype nfsv3_type[9] = {
77	NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON
78};
79
80static void *nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos,
81    int how);
82
83SYSCTL_NODE(_vfs, OID_AUTO, nfs_common, CTLFLAG_RD, 0, "NFS common support");
84
85static int nfs_realign_test;
86SYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_test, CTLFLAG_RD,
87    &nfs_realign_test, 0, "Number of realign tests done");
88
89static int nfs_realign_count;
90SYSCTL_INT(_vfs_nfs_common, OID_AUTO, realign_count, CTLFLAG_RD,
91    &nfs_realign_count, 0, "Number of mbuf realignments done");
92
93/*
94 * copies mbuf chain to the uio scatter/gather list
95 */
96int
97nfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos)
98{
99	char *mbufcp, *uiocp;
100	int xfer, left, len;
101	struct mbuf *mp;
102	long uiosiz, rem;
103	int error = 0;
104
105	mp = *mrep;
106	mbufcp = *dpos;
107	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
108	rem = nfsm_rndup(siz)-siz;
109	while (siz > 0) {
110		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
111			return (EFBIG);
112		left = uiop->uio_iov->iov_len;
113		uiocp = uiop->uio_iov->iov_base;
114		if (left > siz)
115			left = siz;
116		uiosiz = left;
117		while (left > 0) {
118			while (len == 0) {
119				mp = mp->m_next;
120				if (mp == NULL)
121					return (EBADRPC);
122				mbufcp = mtod(mp, caddr_t);
123				len = mp->m_len;
124			}
125			xfer = (left > len) ? len : left;
126#ifdef notdef
127			/* Not Yet.. */
128			if (uiop->uio_iov->iov_op != NULL)
129				(*(uiop->uio_iov->iov_op))
130				(mbufcp, uiocp, xfer);
131			else
132#endif
133			if (uiop->uio_segflg == UIO_SYSSPACE)
134				bcopy(mbufcp, uiocp, xfer);
135			else
136				copyout(mbufcp, uiocp, xfer);
137			left -= xfer;
138			len -= xfer;
139			mbufcp += xfer;
140			uiocp += xfer;
141			uiop->uio_offset += xfer;
142			uiop->uio_resid -= xfer;
143		}
144		if (uiop->uio_iov->iov_len <= siz) {
145			uiop->uio_iovcnt--;
146			uiop->uio_iov++;
147		} else {
148			uiop->uio_iov->iov_base =
149			    (char *)uiop->uio_iov->iov_base + uiosiz;
150			uiop->uio_iov->iov_len -= uiosiz;
151		}
152		siz -= uiosiz;
153	}
154	*dpos = mbufcp;
155	*mrep = mp;
156	if (rem > 0) {
157		if (len < rem)
158			error = nfs_adv(mrep, dpos, rem, len);
159		else
160			*dpos += rem;
161	}
162	return (error);
163}
164
165/*
166 * Help break down an mbuf chain by setting the first siz bytes contiguous
167 * pointed to by returned val.
168 * This is used by the macros nfsm_dissect for tough
169 * cases. (The macros use the vars. dpos and dpos2)
170 */
171void *
172nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, int how)
173{
174	struct mbuf *mp, *mp2;
175	int siz2, xfer;
176	caddr_t ptr, npos = NULL;
177	void *ret;
178
179	mp = *mdp;
180	while (left == 0) {
181		*mdp = mp = mp->m_next;
182		if (mp == NULL)
183			return (NULL);
184		left = mp->m_len;
185		*dposp = mtod(mp, caddr_t);
186	}
187	if (left >= siz) {
188		ret = *dposp;
189		*dposp += siz;
190	} else if (mp->m_next == NULL) {
191		return (NULL);
192	} else if (siz > MHLEN) {
193		panic("nfs S too big");
194	} else {
195		mp2 = m_get(how, MT_DATA);
196		if (mp2 == NULL)
197			return (NULL);
198		mp2->m_len = siz;
199		mp2->m_next = mp->m_next;
200		mp->m_next = mp2;
201		mp->m_len -= left;
202		mp = mp2;
203		ptr = mtod(mp, caddr_t);
204		ret = ptr;
205		bcopy(*dposp, ptr, left);		/* Copy what was left */
206		siz2 = siz-left;
207		ptr += left;
208		mp2 = mp->m_next;
209		npos = mtod(mp2, caddr_t);
210		/* Loop around copying up the siz2 bytes */
211		while (siz2 > 0) {
212			if (mp2 == NULL)
213				return (NULL);
214			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
215			if (xfer > 0) {
216				bcopy(mtod(mp2, caddr_t), ptr, xfer);
217				mp2->m_data += xfer;
218				mp2->m_len -= xfer;
219				ptr += xfer;
220				siz2 -= xfer;
221			}
222			if (siz2 > 0) {
223				mp2 = mp2->m_next;
224				if (mp2 != NULL)
225					npos = mtod(mp2, caddr_t);
226			}
227		}
228		*mdp = mp2;
229		*dposp = mtod(mp2, caddr_t);
230		if (!nfsm_aligned(*dposp, u_int32_t)) {
231			bcopy(*dposp, npos, mp2->m_len);
232			mp2->m_data = npos;
233			*dposp = npos;
234		}
235	}
236	return (ret);
237}
238
239/*
240 * Advance the position in the mbuf chain.
241 */
242int
243nfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left)
244{
245	struct mbuf *m;
246	int s;
247
248	m = *mdp;
249	s = left;
250	while (s < offs) {
251		offs -= s;
252		m = m->m_next;
253		if (m == NULL)
254			return (EBADRPC);
255		s = m->m_len;
256	}
257	*mdp = m;
258	*dposp = mtod(m, caddr_t)+offs;
259	return (0);
260}
261
262void *
263nfsm_build_xx(int s, struct mbuf **mb, caddr_t *bpos)
264{
265	struct mbuf *mb2;
266	void *ret;
267
268	if (s > M_TRAILINGSPACE(*mb)) {
269		mb2 = m_get(M_WAITOK, MT_DATA);
270		if (s > MLEN)
271			panic("build > MLEN");
272		(*mb)->m_next = mb2;
273		*mb = mb2;
274		(*mb)->m_len = 0;
275		*bpos = mtod(*mb, caddr_t);
276	}
277	ret = *bpos;
278	(*mb)->m_len += s;
279	*bpos += s;
280	return (ret);
281}
282
283void *
284nfsm_dissect_xx(int s, struct mbuf **md, caddr_t *dpos)
285{
286
287	return (nfsm_dissect_xx_sub(s, md, dpos, M_WAITOK));
288}
289
290void *
291nfsm_dissect_xx_nonblock(int s, struct mbuf **md, caddr_t *dpos)
292{
293
294	return (nfsm_dissect_xx_sub(s, md, dpos, M_NOWAIT));
295}
296
297static void *
298nfsm_dissect_xx_sub(int s, struct mbuf **md, caddr_t *dpos, int how)
299{
300	int t1;
301	char *cp2;
302	void *ret;
303
304	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
305	if (t1 >= s) {
306		ret = *dpos;
307		*dpos += s;
308		return (ret);
309	}
310	cp2 = nfsm_disct(md, dpos, s, t1, how);
311	return (cp2);
312}
313
314int
315nfsm_strsiz_xx(int *s, int m, struct mbuf **mb, caddr_t *bpos)
316{
317	u_int32_t *tl;
318
319	tl = nfsm_dissect_xx(NFSX_UNSIGNED, mb, bpos);
320	if (tl == NULL)
321		return (EBADRPC);
322	*s = fxdr_unsigned(int32_t, *tl);
323	if (*s > m)
324		return (EBADRPC);
325	return (0);
326}
327
328int
329nfsm_adv_xx(int s, struct mbuf **md, caddr_t *dpos)
330{
331	int t1;
332
333	t1 = mtod(*md, caddr_t) + (*md)->m_len - *dpos;
334	if (t1 >= s) {
335		*dpos += s;
336		return (0);
337	}
338	t1 = nfs_adv(md, dpos, s, t1);
339	if (t1)
340		return (t1);
341	return (0);
342}
343
344/*
345 * Check for badly aligned mbuf data and realign by copying the unaligned
346 * portion of the data into a new mbuf chain and freeing the portions of the
347 * old chain that were replaced.
348 *
349 * We cannot simply realign the data within the existing mbuf chain because
350 * the underlying buffers may contain other rpc commands and we cannot afford
351 * to overwrite them.
352 *
353 * We would prefer to avoid this situation entirely.  The situation does not
354 * occur with NFS/UDP and is supposed to only occassionally occur with TCP.
355 * Use vfs.nfs_common.realign_count and realign_test to check this.
356 */
357int
358nfs_realign(struct mbuf **pm, int how)
359{
360	struct mbuf *m, *n;
361	int off;
362
363	++nfs_realign_test;
364	while ((m = *pm) != NULL) {
365		if (!nfsm_aligned(m->m_len, u_int32_t) ||
366		    !nfsm_aligned(mtod(m, intptr_t), u_int32_t)) {
367			/*
368			 * NB: we can't depend on m_pkthdr.len to help us
369			 * decide what to do here.  May not be worth doing
370			 * the m_length calculation as m_copyback will
371			 * expand the mbuf chain below as needed.
372			 */
373			if (m_length(m, NULL) >= MINCLSIZE) {
374				/* NB: m_copyback handles space > MCLBYTES */
375				n = m_getcl(how, MT_DATA, 0);
376			} else
377				n = m_get(how, MT_DATA);
378			if (n == NULL)
379				return (ENOMEM);
380			/*
381			 * Align the remainder of the mbuf chain.
382			 */
383			n->m_len = 0;
384			off = 0;
385			while (m != NULL) {
386				m_copyback(n, off, m->m_len, mtod(m, caddr_t));
387				off += m->m_len;
388				m = m->m_next;
389			}
390			m_freem(*pm);
391			*pm = n;
392			++nfs_realign_count;
393			break;
394		}
395		pm = &m->m_next;
396	}
397	return (0);
398}
399
400static moduledata_t nfs_common_mod = {
401	"nfs_common",
402	NULL,
403	NULL
404};
405
406DECLARE_MODULE(nfs_common, nfs_common_mod, SI_SUB_VFS, SI_ORDER_ANY);
407MODULE_VERSION(nfs_common, 1);
408