xref: /csrg-svn/sys/nfs/nfs_subs.c (revision 54739)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * 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  * %sccs.include.redist.c%
9  *
10  *	@(#)nfs_subs.c	7.58 (Berkeley) 07/06/92
11  */
12 
13 /*
14  * These functions support the macros and help fiddle mbuf chains for
15  * the nfs op functions. They do things like create the rpc header and
16  * copy data between mbuf chains and uio lists.
17  */
18 #include <sys/param.h>
19 #include <sys/proc.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/mount.h>
23 #include <sys/vnode.h>
24 #include <sys/namei.h>
25 #include <sys/mbuf.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 
29 #include <nfs/rpcv2.h>
30 #include <nfs/nfsv2.h>
31 #include <nfs/nfsnode.h>
32 #include <nfs/nfs.h>
33 #include <nfs/xdr_subs.h>
34 #include <nfs/nfsm_subs.h>
35 #include <nfs/nfsmount.h>
36 #include <nfs/nqnfs.h>
37 #include <nfs/nfsrtt.h>
38 
39 #define TRUE	1
40 #define	FALSE	0
41 
42 /*
43  * Data items converted to xdr at startup, since they are constant
44  * This is kinda hokey, but may save a little time doing byte swaps
45  */
46 u_long nfs_procids[NFS_NPROCS];
47 u_long nfs_xdrneg1;
48 u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
49 	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
50 	rpc_auth_kerb;
51 u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
52 
53 /* And other global data */
54 static u_long nfs_xid = 0;
55 enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
56 extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
57 extern struct nfsreq nfsreqh;
58 extern int nqnfs_piggy[NFS_NPROCS];
59 extern struct nfsrtt nfsrtt;
60 extern union nqsrvthead nqthead;
61 extern union nqsrvthead nqfhead[NQLCHSZ];
62 extern time_t nqnfsstarttime;
63 extern u_long nqnfs_prog, nqnfs_vers;
64 extern int nqsrv_clockskew;
65 extern int nqsrv_writeslack;
66 extern int nqsrv_maxlease;
67 
68 /*
69  * Create the header for an rpc request packet
70  * The hsiz is the size of the rest of the nfs request header.
71  * (just used to decide if a cluster is a good idea)
72  */
73 struct mbuf *
74 nfsm_reqh(vp, procid, hsiz, bposp)
75 	struct vnode *vp;
76 	u_long procid;
77 	int hsiz;
78 	caddr_t *bposp;
79 {
80 	register struct mbuf *mb;
81 	register u_long *tl;
82 	register caddr_t bpos;
83 	struct mbuf *mb2;
84 	struct nfsmount *nmp;
85 	int nqflag;
86 
87 	MGET(mb, M_WAIT, MT_DATA);
88 	if (hsiz >= MINCLSIZE)
89 		MCLGET(mb, M_WAIT);
90 	mb->m_len = 0;
91 	bpos = mtod(mb, caddr_t);
92 
93 	/*
94 	 * For NQNFS, add lease request.
95 	 */
96 	if (vp) {
97 		nmp = VFSTONFS(vp->v_mount);
98 		if (nmp->nm_flag & NFSMNT_NQNFS) {
99 			nqflag = NQNFS_NEEDLEASE(vp, procid);
100 			if (nqflag) {
101 				nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
102 				*tl++ = txdr_unsigned(nqflag);
103 				*tl = txdr_unsigned(nmp->nm_leaseterm);
104 			} else {
105 				nfsm_build(tl, u_long *, NFSX_UNSIGNED);
106 				*tl = 0;
107 			}
108 		}
109 	}
110 	/* Finally, return values */
111 	*bposp = bpos;
112 	return (mb);
113 }
114 
115 /*
116  * Build the RPC header and fill in the authorization info.
117  * The authorization string argument is only used when the credentials
118  * come from outside of the kernel.
119  * Returns the head of the mbuf list.
120  */
121 struct mbuf *
122 nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
123 	mrest_len, mbp, xidp)
124 	register struct ucred *cr;
125 	int nqnfs;
126 	int procid;
127 	int auth_type;
128 	int auth_len;
129 	char *auth_str;
130 	struct mbuf *mrest;
131 	int mrest_len;
132 	struct mbuf **mbp;
133 	u_long *xidp;
134 {
135 	register struct mbuf *mb;
136 	register u_long *tl;
137 	register caddr_t bpos;
138 	register int i;
139 	struct mbuf *mreq, *mb2;
140 	int siz, grpsiz, authsiz;
141 
142 	authsiz = nfsm_rndup(auth_len);
143 	if (auth_type == RPCAUTH_NQNFS)
144 		authsiz += 2 * NFSX_UNSIGNED;
145 	MGETHDR(mb, M_WAIT, MT_DATA);
146 	if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
147 		MCLGET(mb, M_WAIT);
148 	} else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
149 		MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
150 	} else {
151 		MH_ALIGN(mb, 8*NFSX_UNSIGNED);
152 	}
153 	mb->m_len = 0;
154 	mreq = mb;
155 	bpos = mtod(mb, caddr_t);
156 
157 	/*
158 	 * First the RPC header.
159 	 */
160 	nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
161 	if (++nfs_xid == 0)
162 		nfs_xid++;
163 	*tl++ = *xidp = txdr_unsigned(nfs_xid);
164 	*tl++ = rpc_call;
165 	*tl++ = rpc_vers;
166 	if (nqnfs) {
167 		*tl++ = txdr_unsigned(NQNFS_PROG);
168 		*tl++ = txdr_unsigned(NQNFS_VER1);
169 	} else {
170 		*tl++ = txdr_unsigned(NFS_PROG);
171 		*tl++ = txdr_unsigned(NFS_VER2);
172 	}
173 	*tl++ = txdr_unsigned(procid);
174 
175 	/*
176 	 * And then the authorization cred.
177 	 */
178 	*tl++ = txdr_unsigned(auth_type);
179 	*tl = txdr_unsigned(authsiz);
180 	switch (auth_type) {
181 	case RPCAUTH_UNIX:
182 		nfsm_build(tl, u_long *, auth_len);
183 		*tl++ = 0;		/* stamp ?? */
184 		*tl++ = 0;		/* NULL hostname */
185 		*tl++ = txdr_unsigned(cr->cr_uid);
186 		*tl++ = txdr_unsigned(cr->cr_groups[0]);
187 		grpsiz = (auth_len >> 2) - 5;
188 		*tl++ = txdr_unsigned(grpsiz);
189 		for (i = 1; i <= grpsiz; i++)
190 			*tl++ = txdr_unsigned(cr->cr_groups[i]);
191 		break;
192 	case RPCAUTH_NQNFS:
193 		nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
194 		*tl++ = txdr_unsigned(cr->cr_uid);
195 		*tl = txdr_unsigned(auth_len);
196 		siz = auth_len;
197 		while (siz > 0) {
198 			if (M_TRAILINGSPACE(mb) == 0) {
199 				MGET(mb2, M_WAIT, MT_DATA);
200 				if (siz >= MINCLSIZE)
201 					MCLGET(mb2, M_WAIT);
202 				mb->m_next = mb2;
203 				mb = mb2;
204 				mb->m_len = 0;
205 				bpos = mtod(mb, caddr_t);
206 			}
207 			i = MIN(siz, M_TRAILINGSPACE(mb));
208 			bcopy(auth_str, bpos, i);
209 			mb->m_len += i;
210 			auth_str += i;
211 			bpos += i;
212 			siz -= i;
213 		}
214 		if ((siz = nfsm_rndup(auth_len) - auth_len) > 0) {
215 			for (i = 0; i < siz; i++)
216 				*bpos++ = '\0';
217 			mb->m_len += siz;
218 		}
219 		break;
220 	};
221 	nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
222 	*tl++ = txdr_unsigned(RPCAUTH_NULL);
223 	*tl = 0;
224 	mb->m_next = mrest;
225 	mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
226 	mreq->m_pkthdr.rcvif = (struct ifnet *)0;
227 	*mbp = mb;
228 	return (mreq);
229 }
230 
231 /*
232  * copies mbuf chain to the uio scatter/gather list
233  */
234 nfsm_mbuftouio(mrep, uiop, siz, dpos)
235 	struct mbuf **mrep;
236 	register struct uio *uiop;
237 	int siz;
238 	caddr_t *dpos;
239 {
240 	register char *mbufcp, *uiocp;
241 	register int xfer, left, len;
242 	register struct mbuf *mp;
243 	long uiosiz, rem;
244 	int error = 0;
245 
246 	mp = *mrep;
247 	mbufcp = *dpos;
248 	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
249 	rem = nfsm_rndup(siz)-siz;
250 	while (siz > 0) {
251 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
252 			return (EFBIG);
253 		left = uiop->uio_iov->iov_len;
254 		uiocp = uiop->uio_iov->iov_base;
255 		if (left > siz)
256 			left = siz;
257 		uiosiz = left;
258 		while (left > 0) {
259 			while (len == 0) {
260 				mp = mp->m_next;
261 				if (mp == NULL)
262 					return (EBADRPC);
263 				mbufcp = mtod(mp, caddr_t);
264 				len = mp->m_len;
265 			}
266 			xfer = (left > len) ? len : left;
267 #ifdef notdef
268 			/* Not Yet.. */
269 			if (uiop->uio_iov->iov_op != NULL)
270 				(*(uiop->uio_iov->iov_op))
271 				(mbufcp, uiocp, xfer);
272 			else
273 #endif
274 			if (uiop->uio_segflg == UIO_SYSSPACE)
275 				bcopy(mbufcp, uiocp, xfer);
276 			else
277 				copyout(mbufcp, uiocp, xfer);
278 			left -= xfer;
279 			len -= xfer;
280 			mbufcp += xfer;
281 			uiocp += xfer;
282 			uiop->uio_offset += xfer;
283 			uiop->uio_resid -= xfer;
284 		}
285 		if (uiop->uio_iov->iov_len <= siz) {
286 			uiop->uio_iovcnt--;
287 			uiop->uio_iov++;
288 		} else {
289 			uiop->uio_iov->iov_base += uiosiz;
290 			uiop->uio_iov->iov_len -= uiosiz;
291 		}
292 		siz -= uiosiz;
293 	}
294 	*dpos = mbufcp;
295 	*mrep = mp;
296 	if (rem > 0) {
297 		if (len < rem)
298 			error = nfs_adv(mrep, dpos, rem, len);
299 		else
300 			*dpos += rem;
301 	}
302 	return (error);
303 }
304 
305 /*
306  * copies a uio scatter/gather list to an mbuf chain...
307  */
308 nfsm_uiotombuf(uiop, mq, siz, bpos)
309 	register struct uio *uiop;
310 	struct mbuf **mq;
311 	int siz;
312 	caddr_t *bpos;
313 {
314 	register char *uiocp;
315 	register struct mbuf *mp, *mp2;
316 	register int xfer, left, mlen;
317 	int uiosiz, clflg, rem;
318 	char *cp;
319 
320 	if (siz > MLEN)		/* or should it >= MCLBYTES ?? */
321 		clflg = 1;
322 	else
323 		clflg = 0;
324 	rem = nfsm_rndup(siz)-siz;
325 	mp = mp2 = *mq;
326 	while (siz > 0) {
327 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
328 			return (EINVAL);
329 		left = uiop->uio_iov->iov_len;
330 		uiocp = uiop->uio_iov->iov_base;
331 		if (left > siz)
332 			left = siz;
333 		uiosiz = left;
334 		while (left > 0) {
335 			mlen = M_TRAILINGSPACE(mp);
336 			if (mlen == 0) {
337 				MGET(mp, M_WAIT, MT_DATA);
338 				if (clflg)
339 					MCLGET(mp, M_WAIT);
340 				mp->m_len = 0;
341 				mp2->m_next = mp;
342 				mp2 = mp;
343 				mlen = M_TRAILINGSPACE(mp);
344 			}
345 			xfer = (left > mlen) ? mlen : left;
346 #ifdef notdef
347 			/* Not Yet.. */
348 			if (uiop->uio_iov->iov_op != NULL)
349 				(*(uiop->uio_iov->iov_op))
350 				(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
351 			else
352 #endif
353 			if (uiop->uio_segflg == UIO_SYSSPACE)
354 				bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
355 			else
356 				copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
357 			mp->m_len += xfer;
358 			left -= xfer;
359 			uiocp += xfer;
360 			uiop->uio_offset += xfer;
361 			uiop->uio_resid -= xfer;
362 		}
363 		if (uiop->uio_iov->iov_len <= siz) {
364 			uiop->uio_iovcnt--;
365 			uiop->uio_iov++;
366 		} else {
367 			uiop->uio_iov->iov_base += uiosiz;
368 			uiop->uio_iov->iov_len -= uiosiz;
369 		}
370 		siz -= uiosiz;
371 	}
372 	if (rem > 0) {
373 		if (rem > M_TRAILINGSPACE(mp)) {
374 			MGET(mp, M_WAIT, MT_DATA);
375 			mp->m_len = 0;
376 			mp2->m_next = mp;
377 		}
378 		cp = mtod(mp, caddr_t)+mp->m_len;
379 		for (left = 0; left < rem; left++)
380 			*cp++ = '\0';
381 		mp->m_len += rem;
382 		*bpos = cp;
383 	} else
384 		*bpos = mtod(mp, caddr_t)+mp->m_len;
385 	*mq = mp;
386 	return (0);
387 }
388 
389 /*
390  * Help break down an mbuf chain by setting the first siz bytes contiguous
391  * pointed to by returned val.
392  * If Updateflg == True we can overwrite the first part of the mbuf data
393  * (in this case it can never sleep, so it can be called from interrupt level)
394  * it may however block when Updateflg == False
395  * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
396  * cases. (The macros use the vars. dpos and dpos2)
397  */
398 nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
399 	struct mbuf **mdp;
400 	caddr_t *dposp;
401 	int siz;
402 	int left;
403 	int updateflg;
404 	caddr_t *cp2;
405 {
406 	register struct mbuf *mp, *mp2;
407 	register int siz2, xfer;
408 	register caddr_t p;
409 
410 	mp = *mdp;
411 	while (left == 0) {
412 		*mdp = mp = mp->m_next;
413 		if (mp == NULL)
414 			return (EBADRPC);
415 		left = mp->m_len;
416 		*dposp = mtod(mp, caddr_t);
417 	}
418 	if (left >= siz) {
419 		*cp2 = *dposp;
420 		*dposp += siz;
421 	} else if (mp->m_next == NULL) {
422 		return (EBADRPC);
423 	} else if (siz > MHLEN) {
424 		panic("nfs S too big");
425 	} else {
426 		/* Iff update, you can overwrite, else must alloc new mbuf */
427 		if (updateflg) {
428 			NFSMINOFF(mp);
429 		} else {
430 			MGET(mp2, M_WAIT, MT_DATA);
431 			mp2->m_next = mp->m_next;
432 			mp->m_next = mp2;
433 			mp->m_len -= left;
434 			mp = mp2;
435 		}
436 		*cp2 = p = mtod(mp, caddr_t);
437 		bcopy(*dposp, p, left);		/* Copy what was left */
438 		siz2 = siz-left;
439 		p += left;
440 		mp2 = mp->m_next;
441 		/* Loop around copying up the siz2 bytes */
442 		while (siz2 > 0) {
443 			if (mp2 == NULL)
444 				return (EBADRPC);
445 			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
446 			if (xfer > 0) {
447 				bcopy(mtod(mp2, caddr_t), p, xfer);
448 				NFSMADV(mp2, xfer);
449 				mp2->m_len -= xfer;
450 				p += xfer;
451 				siz2 -= xfer;
452 			}
453 			if (siz2 > 0)
454 				mp2 = mp2->m_next;
455 		}
456 		mp->m_len = siz;
457 		*mdp = mp2;
458 		*dposp = mtod(mp2, caddr_t);
459 	}
460 	return (0);
461 }
462 
463 /*
464  * Advance the position in the mbuf chain.
465  */
466 nfs_adv(mdp, dposp, offs, left)
467 	struct mbuf **mdp;
468 	caddr_t *dposp;
469 	int offs;
470 	int left;
471 {
472 	register struct mbuf *m;
473 	register int s;
474 
475 	m = *mdp;
476 	s = left;
477 	while (s < offs) {
478 		offs -= s;
479 		m = m->m_next;
480 		if (m == NULL)
481 			return (EBADRPC);
482 		s = m->m_len;
483 	}
484 	*mdp = m;
485 	*dposp = mtod(m, caddr_t)+offs;
486 	return (0);
487 }
488 
489 /*
490  * Copy a string into mbufs for the hard cases...
491  */
492 nfsm_strtmbuf(mb, bpos, cp, siz)
493 	struct mbuf **mb;
494 	char **bpos;
495 	char *cp;
496 	long siz;
497 {
498 	register struct mbuf *m1, *m2;
499 	long left, xfer, len, tlen;
500 	u_long *tl;
501 	int putsize;
502 
503 	putsize = 1;
504 	m2 = *mb;
505 	left = M_TRAILINGSPACE(m2);
506 	if (left > 0) {
507 		tl = ((u_long *)(*bpos));
508 		*tl++ = txdr_unsigned(siz);
509 		putsize = 0;
510 		left -= NFSX_UNSIGNED;
511 		m2->m_len += NFSX_UNSIGNED;
512 		if (left > 0) {
513 			bcopy(cp, (caddr_t) tl, left);
514 			siz -= left;
515 			cp += left;
516 			m2->m_len += left;
517 			left = 0;
518 		}
519 	}
520 	/* Loop around adding mbufs */
521 	while (siz > 0) {
522 		MGET(m1, M_WAIT, MT_DATA);
523 		if (siz > MLEN)
524 			MCLGET(m1, M_WAIT);
525 		m1->m_len = NFSMSIZ(m1);
526 		m2->m_next = m1;
527 		m2 = m1;
528 		tl = mtod(m1, u_long *);
529 		tlen = 0;
530 		if (putsize) {
531 			*tl++ = txdr_unsigned(siz);
532 			m1->m_len -= NFSX_UNSIGNED;
533 			tlen = NFSX_UNSIGNED;
534 			putsize = 0;
535 		}
536 		if (siz < m1->m_len) {
537 			len = nfsm_rndup(siz);
538 			xfer = siz;
539 			if (xfer < len)
540 				*(tl+(xfer>>2)) = 0;
541 		} else {
542 			xfer = len = m1->m_len;
543 		}
544 		bcopy(cp, (caddr_t) tl, xfer);
545 		m1->m_len = len+tlen;
546 		siz -= xfer;
547 		cp += xfer;
548 	}
549 	*mb = m1;
550 	*bpos = mtod(m1, caddr_t)+m1->m_len;
551 	return (0);
552 }
553 
554 /*
555  * Called once to initialize data structures...
556  */
557 nfs_init()
558 {
559 	register int i;
560 	union nqsrvthead *lhp;
561 
562 	nfsrtt.pos = 0;
563 	rpc_vers = txdr_unsigned(RPC_VER2);
564 	rpc_call = txdr_unsigned(RPC_CALL);
565 	rpc_reply = txdr_unsigned(RPC_REPLY);
566 	rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
567 	rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
568 	rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
569 	rpc_autherr = txdr_unsigned(RPC_AUTHERR);
570 	rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
571 	rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
572 	rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
573 	nfs_vers = txdr_unsigned(NFS_VER2);
574 	nfs_prog = txdr_unsigned(NFS_PROG);
575 	nfs_true = txdr_unsigned(TRUE);
576 	nfs_false = txdr_unsigned(FALSE);
577 	/* Loop thru nfs procids */
578 	for (i = 0; i < NFS_NPROCS; i++)
579 		nfs_procids[i] = txdr_unsigned(i);
580 	/* Ensure async daemons disabled */
581 	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
582 		nfs_iodwant[i] = (struct proc *)0;
583 	nfs_xdrneg1 = txdr_unsigned(-1);
584 	nfs_nhinit();			/* Init the nfsnode table */
585 	nfsrv_init(0);			/* Init server data structures */
586 	nfsrv_initcache();		/* Init the server request cache */
587 
588 	/*
589 	 * Initialize the nqnfs server stuff.
590 	 */
591 	if (nqnfsstarttime == 0) {
592 		nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
593 			+ nqsrv_clockskew + nqsrv_writeslack;
594 		NQLOADNOVRAM(nqnfsstarttime);
595 		nqnfs_prog = txdr_unsigned(NQNFS_PROG);
596 		nqnfs_vers = txdr_unsigned(NQNFS_VER1);
597 		nqthead.th_head[0] = &nqthead;
598 		nqthead.th_head[1] = &nqthead;
599 		for (i = 0; i < NQLCHSZ; i++) {
600 			lhp = &nqfhead[i];
601 			lhp->th_head[0] = lhp;
602 			lhp->th_head[1] = lhp;
603 		}
604 	}
605 
606 	/*
607 	 * Initialize reply list and start timer
608 	 */
609 	nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
610 	nfs_timer();
611 }
612 
613 /*
614  * Attribute cache routines.
615  * nfs_loadattrcache() - loads or updates the cache contents from attributes
616  *	that are on the mbuf list
617  * nfs_getattrcache() - returns valid attributes if found in cache, returns
618  *	error otherwise
619  */
620 
621 /*
622  * Load the attribute cache (that lives in the nfsnode entry) with
623  * the values on the mbuf list and
624  * Iff vap not NULL
625  *    copy the attributes to *vaper
626  */
627 nfs_loadattrcache(vpp, mdp, dposp, vaper)
628 	struct vnode **vpp;
629 	struct mbuf **mdp;
630 	caddr_t *dposp;
631 	struct vattr *vaper;
632 {
633 	register struct vnode *vp = *vpp;
634 	register struct vattr *vap;
635 	register struct nfsv2_fattr *fp;
636 	extern int (**spec_nfsv2nodeop_p)();
637 	extern int (**spec_vnodeop_p)();
638 	register struct nfsnode *np;
639 	register long t1;
640 	caddr_t dpos, cp2;
641 	int error = 0;
642 	struct mbuf *md;
643 	enum vtype vtyp;
644 	u_short vmode;
645 	long rdev;
646 	struct timeval mtime;
647 	struct vnode *nvp;
648 
649 	md = *mdp;
650 	dpos = *dposp;
651 	t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
652 	if (error = nfsm_disct(&md, &dpos, NFSX_FATTR, t1, TRUE, &cp2))
653 		return (error);
654 	fp = (struct nfsv2_fattr *)cp2;
655 	vtyp = nfstov_type(fp->fa_type);
656 	vmode = fxdr_unsigned(u_short, fp->fa_mode);
657 	if (vtyp == VNON || vtyp == VREG)
658 		vtyp = IFTOVT(vmode);
659 	rdev = fxdr_unsigned(long, fp->fa_rdev);
660 	fxdr_time(&fp->fa_mtime, &mtime);
661 	/*
662 	 * If v_type == VNON it is a new node, so fill in the v_type,
663 	 * n_mtime fields. Check to see if it represents a special
664 	 * device, and if so, check for a possible alias. Once the
665 	 * correct vnode has been obtained, fill in the rest of the
666 	 * information.
667 	 */
668 	np = VTONFS(vp);
669 	if (vp->v_type == VNON) {
670 		if (vtyp == VCHR && rdev == 0xffffffff)
671 			vp->v_type = vtyp = VFIFO;
672 		else
673 			vp->v_type = vtyp;
674 		if (vp->v_type == VFIFO) {
675 #ifdef FIFO
676 			extern int (**fifo_nfsv2nodeop_p)();
677 			vp->v_op = fifo_nfsv2nodeop_p;
678 #else
679 			return (EOPNOTSUPP);
680 #endif /* FIFO */
681 		}
682 		if (vp->v_type == VCHR || vp->v_type == VBLK) {
683 			vp->v_op = spec_nfsv2nodeop_p;
684 			if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
685 				/*
686 				 * Discard unneeded vnode, but save its nfsnode.
687 				 */
688 				remque(np);
689 				nvp->v_data = vp->v_data;
690 				vp->v_data = NULL;
691 				vp->v_op = spec_vnodeop_p;
692 				vrele(vp);
693 				vgone(vp);
694 				/*
695 				 * Reinitialize aliased node.
696 				 */
697 				np->n_vnode = nvp;
698 				insque(np, nfs_hash(&np->n_fh));
699 				*vpp = vp = nvp;
700 			}
701 		}
702 		if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0)
703 			np->n_mtime = mtime.tv_sec;
704 	}
705 	vap = &np->n_vattr;
706 	vap->va_type = vtyp;
707 	vap->va_mode = (vmode & 07777);
708 	vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
709 	vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
710 	vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
711 	vap->va_size = fxdr_unsigned(u_long, fp->fa_size);
712 	if ((np->n_flag & NMODIFIED) == 0 || vap->va_size > np->n_size) {
713 		np->n_size = vap->va_size;
714 		vnode_pager_setsize(vp, (u_long)np->n_size);
715 	}
716 	vap->va_blocksize = fxdr_unsigned(long, fp->fa_blocksize);
717 	vap->va_rdev = (dev_t)rdev;
718 	vap->va_bytes = fxdr_unsigned(long, fp->fa_blocks) * NFS_FABLKSIZE;
719 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
720 	vap->va_fileid = fxdr_unsigned(long, fp->fa_fileid);
721 	vap->va_atime.ts_sec = fxdr_unsigned(long, fp->fa_atime.tv_sec);
722 	vap->va_atime.ts_nsec = 0;
723 	vap->va_flags = fxdr_unsigned(u_long, fp->fa_atime.tv_usec);
724 	vap->va_mtime.ts_sec = mtime.tv_sec;
725 	vap->va_mtime.ts_nsec = mtime.tv_usec * 1000;
726 	vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_ctime.tv_sec);
727 	vap->va_ctime.ts_nsec = 0;
728 	vap->va_gen = fxdr_unsigned(u_long, fp->fa_ctime.tv_usec);
729 	np->n_attrstamp = time.tv_sec;
730 	*dposp = dpos;
731 	*mdp = md;
732 	if (vaper != NULL) {
733 		bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
734 		if ((np->n_flag & NMODIFIED) && (np->n_size > vap->va_size))
735 			vaper->va_size = np->n_size;
736 		if (np->n_flag & NCHG) {
737 			if (np->n_flag & NACC) {
738 				vaper->va_atime.ts_sec = np->n_atim.tv_sec;
739 				vaper->va_atime.ts_nsec =
740 				    np->n_atim.tv_usec * 1000;
741 			}
742 			if (np->n_flag & NUPD) {
743 				vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
744 				vaper->va_mtime.ts_nsec =
745 				    np->n_mtim.tv_usec * 1000;
746 			}
747 		}
748 	}
749 	return (0);
750 }
751 
752 /*
753  * Check the time stamp
754  * If the cache is valid, copy contents to *vap and return 0
755  * otherwise return an error
756  */
757 nfs_getattrcache(vp, vap)
758 	register struct vnode *vp;
759 	struct vattr *vap;
760 {
761 	register struct nfsnode *np;
762 
763 	np = VTONFS(vp);
764 	if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) {
765 		if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
766 			nfsstats.attrcache_misses++;
767 			return (ENOENT);
768 		}
769 	} else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO) {
770 		nfsstats.attrcache_misses++;
771 		return (ENOENT);
772 	}
773 	nfsstats.attrcache_hits++;
774 	bcopy((caddr_t)&np->n_vattr,(caddr_t)vap,sizeof(struct vattr));
775 	if ((np->n_flag & NMODIFIED) == 0) {
776 		np->n_size = vap->va_size;
777 		vnode_pager_setsize(vp, (u_long)np->n_size);
778 	} else if (np->n_size > vap->va_size)
779 		vap->va_size = np->n_size;
780 	if (np->n_flag & NCHG) {
781 		if (np->n_flag & NACC) {
782 			vap->va_atime.ts_sec = np->n_atim.tv_sec;
783 			vap->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
784 		}
785 		if (np->n_flag & NUPD) {
786 			vap->va_mtime.ts_sec = np->n_mtim.tv_sec;
787 			vap->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
788 		}
789 	}
790 	return (0);
791 }
792 
793 /*
794  * Set up nameidata for a lookup() call and do it
795  */
796 nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
797 	register struct nameidata *ndp;
798 	fhandle_t *fhp;
799 	int len;
800 	struct nfssvc_sock *slp;
801 	struct mbuf *nam;
802 	struct mbuf **mdp;
803 	caddr_t *dposp;
804 	struct proc *p;
805 {
806 	register int i, rem;
807 	register struct mbuf *md;
808 	register char *fromcp, *tocp;
809 	struct vnode *dp;
810 	int error, rdonly;
811 	struct componentname *cnp = &ndp->ni_cnd;
812 
813 	MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
814 	/*
815 	 * Copy the name from the mbuf list to ndp->ni_pnbuf
816 	 * and set the various ndp fields appropriately.
817 	 */
818 	fromcp = *dposp;
819 	tocp = cnp->cn_pnbuf;
820 	md = *mdp;
821 	rem = mtod(md, caddr_t) + md->m_len - fromcp;
822 	cnp->cn_hash = 0;
823 	for (i = 0; i < len; i++) {
824 		while (rem == 0) {
825 			md = md->m_next;
826 			if (md == NULL) {
827 				error = EBADRPC;
828 				goto out;
829 			}
830 			fromcp = mtod(md, caddr_t);
831 			rem = md->m_len;
832 		}
833 		if (*fromcp == '\0' || *fromcp == '/') {
834 			error = EINVAL;
835 			goto out;
836 		}
837 		if (*fromcp & 0200)
838 			if ((*fromcp&0377) == ('/'|0200) || cnp->cn_nameiop != DELETE) {
839 				error = EINVAL;
840 				goto out;
841 			}
842 		cnp->cn_hash += (unsigned char)*fromcp;
843 		*tocp++ = *fromcp++;
844 		rem--;
845 	}
846 	*tocp = '\0';
847 	*mdp = md;
848 	*dposp = fromcp;
849 	len = nfsm_rndup(len)-len;
850 	if (len > 0) {
851 		if (rem >= len)
852 			*dposp += len;
853 		else if (error = nfs_adv(mdp, dposp, len, rem))
854 			goto out;
855 	}
856 	ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
857 	cnp->cn_nameptr = cnp->cn_pnbuf;
858 	/*
859 	 * Extract and set starting directory.
860 	 */
861 	if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
862 	    nam, &rdonly))
863 		goto out;
864 	if (dp->v_type != VDIR) {
865 		vrele(dp);
866 		error = ENOTDIR;
867 		goto out;
868 	}
869 	ndp->ni_startdir = dp;
870 	if (rdonly)
871 		cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
872 	else
873 		cnp->cn_flags |= NOCROSSMOUNT;
874 	/*
875 	 * And call lookup() to do the real work
876 	 */
877 	cnp->cn_proc = p;
878 	if (error = lookup(ndp))
879 		goto out;
880 	/*
881 	 * Check for encountering a symbolic link
882 	 */
883 	if (cnp->cn_flags & ISSYMLINK) {
884 		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
885 			vput(ndp->ni_dvp);
886 		else
887 			vrele(ndp->ni_dvp);
888 		vput(ndp->ni_vp);
889 		ndp->ni_vp = NULL;
890 		error = EINVAL;
891 		goto out;
892 	}
893 	/*
894 	 * Check for saved name request
895 	 */
896 	if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
897 		cnp->cn_flags |= HASBUF;
898 		return (0);
899 	}
900 out:
901 	FREE(cnp->cn_pnbuf, M_NAMEI);
902 	return (error);
903 }
904 
905 /*
906  * A fiddled version of m_adj() that ensures null fill to a long
907  * boundary and only trims off the back end
908  */
909 void
910 nfsm_adj(mp, len, nul)
911 	struct mbuf *mp;
912 	register int len;
913 	int nul;
914 {
915 	register struct mbuf *m;
916 	register int count, i;
917 	register char *cp;
918 
919 	/*
920 	 * Trim from tail.  Scan the mbuf chain,
921 	 * calculating its length and finding the last mbuf.
922 	 * If the adjustment only affects this mbuf, then just
923 	 * adjust and return.  Otherwise, rescan and truncate
924 	 * after the remaining size.
925 	 */
926 	count = 0;
927 	m = mp;
928 	for (;;) {
929 		count += m->m_len;
930 		if (m->m_next == (struct mbuf *)0)
931 			break;
932 		m = m->m_next;
933 	}
934 	if (m->m_len > len) {
935 		m->m_len -= len;
936 		if (nul > 0) {
937 			cp = mtod(m, caddr_t)+m->m_len-nul;
938 			for (i = 0; i < nul; i++)
939 				*cp++ = '\0';
940 		}
941 		return;
942 	}
943 	count -= len;
944 	if (count < 0)
945 		count = 0;
946 	/*
947 	 * Correct length for chain is "count".
948 	 * Find the mbuf with last data, adjust its length,
949 	 * and toss data from remaining mbufs on chain.
950 	 */
951 	for (m = mp; m; m = m->m_next) {
952 		if (m->m_len >= count) {
953 			m->m_len = count;
954 			if (nul > 0) {
955 				cp = mtod(m, caddr_t)+m->m_len-nul;
956 				for (i = 0; i < nul; i++)
957 					*cp++ = '\0';
958 			}
959 			break;
960 		}
961 		count -= m->m_len;
962 	}
963 	while (m = m->m_next)
964 		m->m_len = 0;
965 }
966 
967 /*
968  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
969  * 	- look up fsid in mount list (if not found ret error)
970  *	- get vp and export rights by calling VFS_FHTOVP()
971  *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
972  *	- if not lockflag unlock it with VOP_UNLOCK()
973  */
974 nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
975 	fhandle_t *fhp;
976 	int lockflag;
977 	struct vnode **vpp;
978 	struct ucred *cred;
979 	struct nfssvc_sock *slp;
980 	struct mbuf *nam;
981 	int *rdonlyp;
982 {
983 	register struct mount *mp;
984 	register struct nfsuid *uidp;
985 	struct ucred *credanon;
986 	int error, exflags;
987 
988 	*vpp = (struct vnode *)0;
989 	if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
990 		return (ESTALE);
991 	if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
992 		return (error);
993 	/*
994 	 * Check/setup credentials.
995 	 */
996 	if (exflags & MNT_EXKERB) {
997 		uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
998 		while (uidp) {
999 			if (uidp->nu_uid == cred->cr_uid)
1000 				break;
1001 			uidp = uidp->nu_hnext;
1002 		}
1003 		if (uidp) {
1004 			if (cred->cr_ref != 1)
1005 				panic("nsrv fhtovp");
1006 			*cred = uidp->nu_cr;
1007 		} else
1008 			return (NQNFS_AUTHERR);
1009 	} else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON))
1010 		*cred = *credanon;
1011 	if (exflags & MNT_EXRDONLY)
1012 		*rdonlyp = 1;
1013 	else
1014 		*rdonlyp = 0;
1015 	if (!lockflag)
1016 		VOP_UNLOCK(*vpp);
1017 	return (0);
1018 }
1019