xref: /csrg-svn/sys/nfs/nfs_subs.c (revision 40153)
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  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  *	@(#)nfs_subs.c	7.18 (Berkeley) 02/17/90
21  */
22 
23 /*
24  * These functions support the macros and help fiddle mbuf chains for
25  * the nfs op functions. They do things like create the rpc header and
26  * copy data between mbuf chains and uio lists.
27  */
28 #include "param.h"
29 #include "user.h"
30 #include "proc.h"
31 #include "systm.h"
32 #include "kernel.h"
33 #include "mount.h"
34 #include "file.h"
35 #include "vnode.h"
36 #include "mbuf.h"
37 #include "errno.h"
38 #include "map.h"
39 #include "rpcv2.h"
40 #include "nfsv2.h"
41 #include "nfsnode.h"
42 #include "nfs.h"
43 #include "nfsiom.h"
44 #include "xdr_subs.h"
45 #include "nfsm_subs.h"
46 
47 #define TRUE	1
48 #define	FALSE	0
49 
50 /*
51  * Data items converted to xdr at startup, since they are constant
52  * This is kinda hokey, but may save a little time doing byte swaps
53  */
54 u_long nfs_procids[NFS_NPROCS];
55 u_long nfs_xdrneg1;
56 u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied,
57 	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
58 u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
59 /* And other global data */
60 static u_long *rpc_uidp = (u_long *)0;
61 static u_long nfs_xid = 1;
62 static char *rpc_unixauth;
63 extern long hostid;
64 extern enum vtype v_type[NFLNK+1];
65 extern struct proc *nfs_iodwant[MAX_ASYNCDAEMON];
66 extern struct map nfsmap[NFS_MSIZ];
67 
68 /* Function ret types */
69 static char *nfs_unixauth();
70 
71 /*
72  * Maximum number of groups passed through to NFS server.
73  * For release 3.X systems, the maximum value is 8.
74  * For release 4.X systems, the maximum value is 10.
75  */
76 int numgrps = 8;
77 
78 /*
79  * Create the header for an rpc request packet
80  * The function nfs_unixauth() creates a unix style authorization string
81  * and returns a ptr to it.
82  * The hsiz is the size of the rest of the nfs request header.
83  * (just used to decide if a cluster is a good idea)
84  * nb: Note that the prog, vers and procid args are already in xdr byte order
85  */
86 struct mbuf *nfsm_reqh(prog, vers, procid, cred, hsiz, bpos, mb, retxid)
87 	u_long prog;
88 	u_long vers;
89 	u_long procid;
90 	struct ucred *cred;
91 	int hsiz;
92 	caddr_t *bpos;
93 	struct mbuf **mb;
94 	u_long *retxid;
95 {
96 	register struct mbuf *mreq, *m;
97 	register u_long *p;
98 	struct mbuf *m1;
99 	char *ap;
100 	int asiz, siz;
101 
102 	NFSMGETHDR(mreq);
103 	asiz = (((cred->cr_ngroups > numgrps) ? numgrps : cred->cr_ngroups)<<2);
104 #ifdef FILLINHOST
105 	asiz += nfsm_rndup(hostnamelen)+(9*NFSX_UNSIGNED);
106 #else
107 	asiz += 9*NFSX_UNSIGNED;
108 #endif
109 
110 	/* If we need a lot, alloc a cluster ?? */
111 	if ((asiz+hsiz+RPC_SIZ) > MHLEN)
112 		NFSMCLGET(mreq, M_WAIT);
113 	mreq->m_len = NFSMSIZ(mreq);
114 	siz = mreq->m_len;
115 	m1 = mreq;
116 	/*
117 	 * Alloc enough mbufs
118 	 * We do it now to avoid all sleeps after the call to nfs_unixauth()
119 	 */
120 	while ((asiz+RPC_SIZ) > siz) {
121 		MGET(m, M_WAIT, MT_DATA);
122 		m1->m_next = m;
123 		m->m_len = MLEN;
124 		siz += MLEN;
125 		m1 = m;
126 	}
127 	p = mtod(mreq, u_long *);
128 	*p++ = *retxid = txdr_unsigned(++nfs_xid);
129 	*p++ = rpc_call;
130 	*p++ = rpc_vers;
131 	*p++ = prog;
132 	*p++ = vers;
133 	*p++ = procid;
134 
135 	/* Now we can call nfs_unixauth() and copy it in */
136 	ap = nfs_unixauth(cred);
137 	m = mreq;
138 	siz = m->m_len-RPC_SIZ;
139 	if (asiz <= siz) {
140 		bcopy(ap, (caddr_t)p, asiz);
141 		m->m_len = asiz+RPC_SIZ;
142 	} else {
143 		bcopy(ap, (caddr_t)p, siz);
144 		ap += siz;
145 		asiz -= siz;
146 		while (asiz > 0) {
147 			siz = (asiz > MLEN) ? MLEN : asiz;
148 			m = m->m_next;
149 			bcopy(ap, mtod(m, caddr_t), siz);
150 			m->m_len = siz;
151 			asiz -= siz;
152 			ap += siz;
153 		}
154 	}
155 
156 	/* Finally, return values */
157 	*mb = m;
158 	*bpos = mtod(m, caddr_t)+m->m_len;
159 	return (mreq);
160 }
161 
162 /*
163  * copies mbuf chain to the uio scatter/gather list
164  */
165 nfsm_mbuftouio(mrep, uiop, siz, dpos)
166 	struct mbuf **mrep;
167 	struct uio *uiop;
168 	int siz;
169 	caddr_t *dpos;
170 {
171 	register int xfer, left, len;
172 	register struct mbuf *mp;
173 	register char *mbufcp, *uiocp;
174 	long uiosiz, rem;
175 
176 	mp = *mrep;
177 	mbufcp = *dpos;
178 	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
179 	rem = nfsm_rndup(siz)-siz;
180 	while (siz > 0) {
181 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
182 			return(EFBIG);
183 		left = uiop->uio_iov->iov_len;
184 		uiocp = uiop->uio_iov->iov_base;
185 		if (left > siz)
186 			left = siz;
187 		uiosiz = left;
188 		while (left > 0) {
189 			while (len == 0) {
190 				mp = mp->m_next;
191 				if (mp == NULL)
192 					return (EBADRPC);
193 				mbufcp = mtod(mp, caddr_t);
194 				len = mp->m_len;
195 			}
196 			xfer = (left > len) ? len : left;
197 #ifdef notdef
198 			/* Not Yet.. */
199 			if (uiop->uio_iov->iov_op != NULL)
200 				(*(uiop->uio_iov->iov_op))
201 				(mbufcp, uiocp, xfer);
202 			else
203 #endif
204 			if (uiop->uio_segflg == UIO_SYSSPACE)
205 				bcopy(mbufcp, uiocp, xfer);
206 			else
207 				copyout(mbufcp, uiocp, xfer);
208 			left -= xfer;
209 			len -= xfer;
210 			mbufcp += xfer;
211 			uiocp += xfer;
212 			uiop->uio_offset += xfer;
213 			uiop->uio_resid -= xfer;
214 		}
215 		if (uiop->uio_iov->iov_len <= siz) {
216 			uiop->uio_iovcnt--;
217 			uiop->uio_iov++;
218 		} else {
219 			uiop->uio_iov->iov_base += uiosiz;
220 			uiop->uio_iov->iov_len -= uiosiz;
221 		}
222 		siz -= uiosiz;
223 	}
224 	if (rem > 0)
225 		mbufcp += rem;
226 	*dpos = mbufcp;
227 	*mrep = mp;
228 	return(0);
229 }
230 
231 /*
232  * copies a uio scatter/gather list to an mbuf chain...
233  */
234 nfsm_uiotombuf(uiop, mq, siz, bpos)
235 	register struct uio *uiop;
236 	struct mbuf **mq;
237 	int siz;
238 	caddr_t *bpos;
239 {
240 	register struct mbuf *mp;
241 	struct mbuf *mp2;
242 	long xfer, left, uiosiz;
243 	int clflg;
244 	int rem, len;
245 	char *cp, *uiocp;
246 
247 	if (siz > MLEN)		/* or should it >= MCLBYTES ?? */
248 		clflg = 1;
249 	else
250 		clflg = 0;
251 	rem = nfsm_rndup(siz)-siz;
252 	mp2 = *mq;
253 	while (siz > 0) {
254 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
255 			return(EINVAL);
256 		left = uiop->uio_iov->iov_len;
257 		uiocp = uiop->uio_iov->iov_base;
258 		if (left > siz)
259 			left = siz;
260 		uiosiz = left;
261 		while (left > 0) {
262 			MGET(mp, M_WAIT, MT_DATA);
263 			if (clflg)
264 				NFSMCLGET(mp, M_WAIT);
265 			mp->m_len = NFSMSIZ(mp);
266 			mp2->m_next = mp;
267 			mp2 = mp;
268 			xfer = (left > mp->m_len) ? mp->m_len : left;
269 #ifdef notdef
270 			/* Not Yet.. */
271 			if (uiop->uio_iov->iov_op != NULL)
272 				(*(uiop->uio_iov->iov_op))
273 				(uiocp, mtod(mp, caddr_t), xfer);
274 			else
275 #endif
276 			if (uiop->uio_segflg == UIO_SYSSPACE)
277 				bcopy(uiocp, mtod(mp, caddr_t), xfer);
278 			else
279 				copyin(uiocp, mtod(mp, caddr_t), xfer);
280 			len = mp->m_len;
281 			mp->m_len = xfer;
282 			left -= xfer;
283 			uiocp += xfer;
284 			uiop->uio_offset += xfer;
285 			uiop->uio_resid -= xfer;
286 		}
287 		if (uiop->uio_iov->iov_len <= siz) {
288 			uiop->uio_iovcnt--;
289 			uiop->uio_iov++;
290 		} else {
291 			uiop->uio_iov->iov_base += uiosiz;
292 			uiop->uio_iov->iov_len -= uiosiz;
293 		}
294 		siz -= uiosiz;
295 	}
296 	if (rem > 0) {
297 		if (rem > (len-mp->m_len)) {
298 			MGET(mp, M_WAIT, MT_DATA);
299 			mp->m_len = 0;
300 			mp2->m_next = mp;
301 		}
302 		cp = mtod(mp, caddr_t)+mp->m_len;
303 		for (left = 0; left < rem; left++)
304 			*cp++ = '\0';
305 		mp->m_len += rem;
306 		*bpos = cp;
307 	} else
308 		*bpos = mtod(mp, caddr_t)+mp->m_len;
309 	*mq = mp;
310 	return(0);
311 }
312 
313 /*
314  * Help break down an mbuf chain by setting the first siz bytes contiguous
315  * pointed to by returned val.
316  * If Updateflg == True we can overwrite the first part of the mbuf data
317  * This is used by the macros nfsm_disect and nfsm_disecton for tough
318  * cases. (The macros use the vars. dpos and dpos2)
319  */
320 nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
321 	struct mbuf **mdp;
322 	caddr_t *dposp;
323 	int siz;
324 	int left;
325 	int updateflg;
326 	caddr_t *cp2;
327 {
328 	register struct mbuf *mp, *mp2;
329 	register int siz2, xfer;
330 	register caddr_t p;
331 
332 	mp = *mdp;
333 	while (left == 0) {
334 		*mdp = mp = mp->m_next;
335 		if (mp == NULL)
336 			return(EBADRPC);
337 		left = mp->m_len;
338 		*dposp = mtod(mp, caddr_t);
339 	}
340 	if (left >= siz) {
341 		*cp2 = *dposp;
342 		*dposp += siz;
343 		return(0);
344 	} else if (mp->m_next == NULL) {
345 		return(EBADRPC);
346 	} else if (siz > MCLBYTES) {
347 		panic("nfs S too big");
348 	} else {
349 		/* Iff update, you can overwrite, else must alloc new mbuf */
350 		if (updateflg) {
351 			NFSMINOFF(mp);
352 		} else {
353 			MGET(mp2, M_WAIT, MT_DATA);
354 			mp2->m_next = mp->m_next;
355 			mp->m_next = mp2;
356 			mp->m_len -= left;
357 			mp = mp2;
358 		}
359 		/* Alloc cluster iff we need it */
360 		if (!M_HASCL(mp) && siz > NFSMSIZ(mp)) {
361 			NFSMCLGET(mp, M_WAIT);
362 			if (!M_HASCL(mp))
363 				return(ENOBUFS);
364 		}
365 		*cp2 = p = mtod(mp, caddr_t);
366 		bcopy(*dposp, p, left);		/* Copy what was left */
367 		siz2 = siz-left;
368 		p += left;
369 		mp2 = mp->m_next;
370 		/* Loop arround copying up the siz2 bytes */
371 		while (siz2 > 0) {
372 			if (mp2 == NULL)
373 				return (EBADRPC);
374 			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
375 			bcopy(mtod(mp2, caddr_t), p, xfer);
376 			NFSMADV(mp2, xfer);
377 			mp2->m_len -= xfer;
378 			siz2 -= xfer;
379 			if (siz2 > 0)
380 				mp2 = mp2->m_next;
381 		}
382 		mp->m_len = siz;
383 		*mdp = mp2;
384 		*dposp = mtod(mp2, caddr_t);
385 	}
386 	return (0);
387 }
388 
389 /*
390  * Advance the position in the mbuf chain with/without freeing mbufs
391  */
392 nfs_adv(mdp, dposp, offs, left)
393 	struct mbuf **mdp;
394 	caddr_t *dposp;
395 	int offs;
396 	int left;
397 {
398 	register struct mbuf *m;
399 	register int s;
400 
401 	m = *mdp;
402 	s = left;
403 	while (s < offs) {
404 		offs -= s;
405 		m = m->m_next;
406 		if (m == NULL)
407 			return(EBADRPC);
408 		s = m->m_len;
409 	}
410 	*mdp = m;
411 	*dposp = mtod(m, caddr_t)+offs;
412 	return(0);
413 }
414 
415 /*
416  * Copy a string into mbufs for the hard cases...
417  */
418 nfsm_strtmbuf(mb, bpos, cp, siz)
419 	struct mbuf **mb;
420 	char **bpos;
421 	char *cp;
422 	long siz;
423 {
424 	register struct mbuf *m1, *m2;
425 	long left, xfer, len, tlen;
426 	u_long *p;
427 	int putsize;
428 
429 	putsize = 1;
430 	m2 = *mb;
431 	left = NFSMSIZ(m2)-m2->m_len;
432 	if (left > 0) {
433 		p = ((u_long *)(*bpos));
434 		*p++ = txdr_unsigned(siz);
435 		putsize = 0;
436 		left -= NFSX_UNSIGNED;
437 		m2->m_len += NFSX_UNSIGNED;
438 		if (left > 0) {
439 			bcopy(cp, (caddr_t) p, left);
440 			siz -= left;
441 			cp += left;
442 			m2->m_len += left;
443 			left = 0;
444 		}
445 	}
446 	/* Loop arround adding mbufs */
447 	while (siz > 0) {
448 		MGET(m1, M_WAIT, MT_DATA);
449 		if (siz > MLEN)
450 			NFSMCLGET(m1, M_WAIT);
451 		m1->m_len = NFSMSIZ(m1);
452 		m2->m_next = m1;
453 		m2 = m1;
454 		p = mtod(m1, u_long *);
455 		tlen = 0;
456 		if (putsize) {
457 			*p++ = txdr_unsigned(siz);
458 			m1->m_len -= NFSX_UNSIGNED;
459 			tlen = NFSX_UNSIGNED;
460 			putsize = 0;
461 		}
462 		if (siz < m1->m_len) {
463 			len = nfsm_rndup(siz);
464 			xfer = siz;
465 			if (xfer < len)
466 				*(p+(xfer>>2)) = 0;
467 		} else {
468 			xfer = len = m1->m_len;
469 		}
470 		bcopy(cp, (caddr_t) p, xfer);
471 		m1->m_len = len+tlen;
472 		siz -= xfer;
473 		cp += xfer;
474 	}
475 	*mb = m1;
476 	*bpos = mtod(m1, caddr_t)+m1->m_len;
477 	return(0);
478 }
479 
480 /*
481  * Called once to initialize data structures...
482  */
483 nfs_init()
484 {
485 	register int i;
486 
487 	rpc_vers = txdr_unsigned(RPC_VER2);
488 	rpc_call = txdr_unsigned(RPC_CALL);
489 	rpc_reply = txdr_unsigned(RPC_REPLY);
490 	rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
491 	rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
492 	rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
493 	rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
494 	nfs_vers = txdr_unsigned(NFS_VER2);
495 	nfs_prog = txdr_unsigned(NFS_PROG);
496 	nfs_true = txdr_unsigned(TRUE);
497 	nfs_false = txdr_unsigned(FALSE);
498 	/* Loop thru nfs procids */
499 	for (i = 0; i < NFS_NPROCS; i++)
500 		nfs_procids[i] = txdr_unsigned(i);
501 	/* Ensure async daemons disabled */
502 	for (i = 0; i < MAX_ASYNCDAEMON; i++)
503 		nfs_iodwant[i] = (struct proc *)0;
504 	v_type[0] = VNON;
505 	v_type[1] = VREG;
506 	v_type[2] = VDIR;
507 	v_type[3] = VBLK;
508 	v_type[4] = VCHR;
509 	v_type[5] = VLNK;
510 	nfs_xdrneg1 = txdr_unsigned(-1);
511 	nfs_nhinit();			/* Init the nfsnode table */
512 	nfsrv_initcache();		/* Init the server request cache */
513 	rminit(nfsmap, (long)NFS_MAPREG, (long)1, "nfs mapreg", NFS_MSIZ);
514 	/* And start timer */
515 	nfs_timer();
516 }
517 
518 /*
519  * Fill in the rest of the rpc_unixauth and return it
520  */
521 static char *nfs_unixauth(cr)
522 	register struct ucred *cr;
523 {
524 	register u_long *p;
525 	register int i;
526 	int ngr;
527 
528 	/* Maybe someday there should be a cache of AUTH_SHORT's */
529 	if ((p = rpc_uidp) == NULL) {
530 #ifdef FILLINHOST
531 		i = nfsm_rndup(hostnamelen)+(19*NFSX_UNSIGNED);
532 #else
533 		i = 19*NFSX_UNSIGNED;
534 #endif
535 		MALLOC(p, u_long *, i, M_TEMP, M_WAITOK);
536 		bzero((caddr_t)p, i);
537 		rpc_unixauth = (caddr_t)p;
538 		*p++ = txdr_unsigned(RPCAUTH_UNIX);
539 		p++;	/* Fill in size later */
540 		*p++ = hostid;
541 #ifdef FILLINHOST
542 		*p++ = txdr_unsigned(hostnamelen);
543 		i = nfsm_rndup(hostnamelen);
544 		bcopy(hostname, (caddr_t)p, hostnamelen);
545 		p += (i>>2);
546 #else
547 		*p++ = 0;
548 #endif
549 		rpc_uidp = p;
550 	}
551 	*p++ = txdr_unsigned(cr->cr_uid);
552 	*p++ = txdr_unsigned(cr->cr_groups[0]);
553 	ngr = (cr->cr_ngroups > numgrps) ? numgrps : cr->cr_ngroups;
554 	*p++ = txdr_unsigned(ngr);
555 	for (i = 0; i < ngr; i++)
556 		*p++ = txdr_unsigned(cr->cr_groups[i]);
557 	/* And add the AUTH_NULL */
558 	*p++ = 0;
559 	*p = 0;
560 	i = (((caddr_t)p)-rpc_unixauth)-12;
561 	p = (u_long *)(rpc_unixauth+4);
562 	*p = txdr_unsigned(i);
563 	return(rpc_unixauth);
564 }
565 
566 /*
567  * Attribute cache routines.
568  * nfs_loadattrcache() - loads or updates the cache contents from attributes
569  *	that are on the mbuf list
570  * nfs_getattrcache() - returns valid attributes if found in cache, returns
571  *	error otherwise
572  */
573 
574 /*
575  * Load the attribute cache (that lives in the nfsnode entry) with
576  * the values on the mbuf list and
577  * Iff vap not NULL
578  *    copy the attributes to *vaper
579  */
580 nfs_loadattrcache(vpp, mdp, dposp, vaper)
581 	struct vnode **vpp;
582 	struct mbuf **mdp;
583 	caddr_t *dposp;
584 	struct vattr *vaper;
585 {
586 	register struct vnode *vp = *vpp;
587 	register struct vattr *vap;
588 	register struct nfsv2_fattr *fp;
589 	extern struct vnodeops spec_nfsv2nodeops;
590 	register struct nfsnode *np;
591 	register long t1;
592 	caddr_t dpos, cp2;
593 	int error = 0;
594 	struct mbuf *md;
595 	enum vtype type;
596 	dev_t rdev;
597 	struct timeval mtime;
598 	struct vnode *nvp;
599 
600 	md = *mdp;
601 	dpos = *dposp;
602 	t1 = (mtod(md, caddr_t)+md->m_len)-dpos;
603 	if (error = nfsm_disct(&md, &dpos, NFSX_FATTR, t1, TRUE, &cp2))
604 		return (error);
605 	fp = (struct nfsv2_fattr *)cp2;
606 	type = nfstov_type(fp->fa_type);
607 	rdev = fxdr_unsigned(dev_t, fp->fa_rdev);
608 	fxdr_time(&fp->fa_mtime, &mtime);
609 	/*
610 	 * If v_type == VNON it is a new node, so fill in the v_type,
611 	 * n_mtime fields. Check to see if it represents a special
612 	 * device, and if so, check for a possible alias. Once the
613 	 * correct vnode has been obtained, fill in the rest of the
614 	 * information.
615 	 */
616 	np = VTONFS(vp);
617 	if (vp->v_type == VNON) {
618 		vp->v_type = type;
619 		if (vp->v_type == VCHR || vp->v_type == VBLK) {
620 			vp->v_op = &spec_nfsv2nodeops;
621 			if (nvp = checkalias(vp, rdev, vp->v_mount)) {
622 				/*
623 				 * Reinitialize aliased node.
624 				 */
625 				np = VTONFS(nvp);
626 				np->n_vnode = nvp;
627 				np->n_flag = 0;
628 				nfs_lock(nvp);
629 				bcopy((caddr_t)&VTONFS(vp)->n_fh,
630 					(caddr_t)&np->n_fh, NFSX_FH);
631 				insque(np, nfs_hash(&np->n_fh));
632 				np->n_attrstamp = 0;
633 				np->n_sillyrename = (struct sillyrename *)0;
634 				/*
635 				 * Discard unneeded vnode and update actual one
636 				 */
637 				vput(vp);
638 				*vpp = nvp;
639 			}
640 		}
641 		np->n_mtime = mtime.tv_sec;
642 	}
643 	vap = &np->n_vattr;
644 	vap->va_type = type;
645 	vap->va_mode = nfstov_mode(fp->fa_mode);
646 	vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
647 	vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
648 	vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
649 	vap->va_size = fxdr_unsigned(u_long, fp->fa_size);
650 	if ((np->n_flag & NMODIFIED) == 0 || vap->va_size > np->n_size)
651 		np->n_size = vap->va_size;
652 	vap->va_size1 = 0;		/* OR -1 ?? */
653 	vap->va_blocksize = fxdr_unsigned(long, fp->fa_blocksize);
654 	vap->va_rdev = rdev;
655 	vap->va_bytes = fxdr_unsigned(long, fp->fa_blocks) * vap->va_blocksize;
656 	vap->va_bytes1 = 0;
657 	vap->va_fsid = vp->v_mount->m_fsid.val[0];
658 	vap->va_fileid = fxdr_unsigned(long, fp->fa_fileid);
659 	vap->va_atime.tv_sec = fxdr_unsigned(long, fp->fa_atime.tv_sec);
660 	vap->va_atime.tv_usec = 0;
661 	vap->va_flags = fxdr_unsigned(u_long, fp->fa_atime.tv_usec);
662 	vap->va_mtime = mtime;
663 	vap->va_ctime.tv_sec = fxdr_unsigned(long, fp->fa_ctime.tv_sec);
664 	vap->va_ctime.tv_usec = 0;
665 	vap->va_gen = fxdr_unsigned(u_long, fp->fa_ctime.tv_usec);
666 	np->n_attrstamp = time.tv_sec;
667 	*dposp = dpos;
668 	*mdp = md;
669 	if (vaper != NULL) {
670 		bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
671 		if ((np->n_flag & NMODIFIED) && (np->n_size > vap->va_size))
672 			vaper->va_size = np->n_size;
673 	}
674 	return (0);
675 }
676 
677 /*
678  * Check the time stamp
679  * If the cache is valid, copy contents to *vap and return 0
680  * otherwise return an error
681  */
682 nfs_getattrcache(vp, vap)
683 	register struct vnode *vp;
684 	struct vattr *vap;
685 {
686 	register struct nfsnode *np;
687 
688 	np = VTONFS(vp);
689 	if ((time.tv_sec-np->n_attrstamp) < NFS_ATTRTIMEO) {
690 		nfsstats.attrcache_hits++;
691 		bcopy((caddr_t)&np->n_vattr,(caddr_t)vap,sizeof(struct vattr));
692 		if ((np->n_flag & NMODIFIED) == 0)
693 			np->n_size = vap->va_size;
694 		else if (np->n_size > vap->va_size)
695 			vap->va_size = np->n_size;
696 		return (0);
697 	} else {
698 		nfsstats.attrcache_misses++;
699 		return (ENOENT);
700 	}
701 }
702 
703 /*
704  * nfs_namei - a liitle like namei(), but for one element only
705  *	essentially look up file handle, fill in ndp and call VOP_LOOKUP()
706  */
707 nfs_namei(ndp, fhp, len, mdp, dposp)
708 	register struct nameidata *ndp;
709 	fhandle_t *fhp;
710 	int len;
711 	struct mbuf **mdp;
712 	caddr_t *dposp;
713 {
714 	register int i, rem;
715 	register struct mbuf *md;
716 	register char *cp;
717 	struct vnode *dp = (struct vnode *)0;
718 	int flag;
719 	int docache;
720 	int wantparent;
721 	int lockparent;
722 	int error = 0;
723 
724 	ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0;
725 	flag = ndp->ni_nameiop & OPFLAG;
726 	wantparent = ndp->ni_nameiop & (LOCKPARENT | WANTPARENT);
727 	lockparent = ndp->ni_nameiop & LOCKPARENT;
728 	docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
729 	if (flag == DELETE || wantparent)
730 		docache = 0;
731 
732 	/* Fill in the nameidata and call lookup */
733 	cp = *dposp;
734 	md = *mdp;
735 	rem = mtod(md, caddr_t)+md->m_len-cp;
736 	ndp->ni_hash = 0;
737 	for (i = 0; i < len;) {
738 		if (rem == 0) {
739 			md = md->m_next;
740 			if (md == NULL)
741 				return (EBADRPC);
742 			cp = mtod(md, caddr_t);
743 			rem = md->m_len;
744 		}
745 		if (*cp == '\0' || *cp == '/')
746 			return (EINVAL);
747 		if (*cp & 0200)
748 			if ((*cp&0377) == ('/'|0200) || flag != DELETE)
749 				return (EINVAL);
750 		ndp->ni_dent.d_name[i++] = *cp;
751 		ndp->ni_hash += (unsigned char)*cp * i;
752 		cp++;
753 		rem--;
754 	}
755 	*mdp = md;
756 	len = nfsm_rndup(len)-len;
757 	if (len > 0)
758 		*dposp = cp+len;
759 	else
760 		*dposp = cp;
761 	ndp->ni_namelen = i;
762 	ndp->ni_dent.d_namlen = i;
763 	ndp->ni_dent.d_name[i] = '\0';
764 	ndp->ni_pathlen = 1;
765 	ndp->ni_dirp = ndp->ni_ptr = &ndp->ni_dent.d_name[0];
766 	ndp->ni_next = &ndp->ni_dent.d_name[i];
767 	ndp->ni_loopcnt = 0;	/* Not actually used for now */
768 	ndp->ni_endoff = 0;
769 	if (docache)
770 		ndp->ni_makeentry = 1;
771 	else
772 		ndp->ni_makeentry = 0;
773 	ndp->ni_isdotdot = (i == 2 &&
774 		ndp->ni_dent.d_name[1] == '.' && ndp->ni_dent.d_name[0] == '.');
775 
776 	if (error = nfsrv_fhtovp(fhp, TRUE, &dp, ndp->ni_cred))
777 		return (error);
778 	if (dp->v_type != VDIR) {
779 		vput(dp);
780 		return (ENOTDIR);
781 	}
782 	/*
783 	 * Must set current directory here to avoid confusion in namei()
784 	 * called from rename()
785 	 */
786 	ndp->ni_cdir = dp;
787 	ndp->ni_rdir = (struct vnode *)0;
788 
789 	/*
790 	 * Handle "..":
791 	 * If this vnode is the root of the mounted
792 	 *    file system, then ignore it so can't get out
793 	 */
794 	if (ndp->ni_isdotdot && (dp->v_flag & VROOT)) {
795 		ndp->ni_dvp = dp;
796 		ndp->ni_vp = dp;
797 		VREF(dp);
798 		goto nextname;
799 	}
800 
801 	/*
802 	 * We now have a segment name to search for, and a directory to search.
803 	 */
804 	if (error = VOP_LOOKUP(dp, ndp)) {
805 		if (ndp->ni_vp != NULL)
806 			panic("leaf should be empty");
807 		/*
808 		 * If creating and at end of pathname, then can consider
809 		 * allowing file to be created.
810 		 */
811 		if (ndp->ni_dvp->v_mount->m_flag & (M_RDONLY | M_EXRDONLY))
812 			error = EROFS;
813 		if (flag == LOOKUP || flag == DELETE || error != ENOENT)
814 			goto bad;
815 		/*
816 		 * We return with ni_vp NULL to indicate that the entry
817 		 * doesn't currently exist, leaving a pointer to the
818 		 * (possibly locked) directory inode in ndp->ni_dvp.
819 		 */
820 		return (0);	/* should this be ENOENT? */
821 	}
822 
823 	dp = ndp->ni_vp;
824 
825 nextname:
826 	ndp->ni_ptr = ndp->ni_next;
827 	/*
828 	 * Check for read-only file systems
829 	 */
830 	if (flag == DELETE || flag == RENAME) {
831 		/*
832 		 * Disallow directory write attempts on read-only
833 		 * file systems.
834 		 */
835 		if ((dp->v_mount->m_flag & (M_RDONLY|M_EXRDONLY)) ||
836 		    (wantparent && (ndp->ni_dvp->v_mount->m_flag & (M_RDONLY|M_EXRDONLY)))) {
837 			error = EROFS;
838 			goto bad2;
839 		}
840 	}
841 
842 	if (!wantparent)
843 		vrele(ndp->ni_dvp);
844 
845 	if ((ndp->ni_nameiop & LOCKLEAF) == 0)
846 		VOP_UNLOCK(dp);
847 	return (0);
848 
849 bad2:
850 	if (lockparent)
851 		VOP_UNLOCK(ndp->ni_dvp);
852 	vrele(ndp->ni_dvp);
853 bad:
854 	vput(dp);
855 	ndp->ni_vp = NULL;
856 	return (error);
857 }
858 
859 /*
860  * A fiddled version of m_adj() that ensures null fill to a long
861  * boundary and only trims off the back end
862  */
863 nfsm_adj(mp, len, nul)
864 	struct mbuf *mp;
865 	register int len;
866 	int nul;
867 {
868 	register struct mbuf *m;
869 	register int count, i;
870 	register char *cp;
871 
872 	/*
873 	 * Trim from tail.  Scan the mbuf chain,
874 	 * calculating its length and finding the last mbuf.
875 	 * If the adjustment only affects this mbuf, then just
876 	 * adjust and return.  Otherwise, rescan and truncate
877 	 * after the remaining size.
878 	 */
879 	count = 0;
880 	m = mp;
881 	for (;;) {
882 		count += m->m_len;
883 		if (m->m_next == (struct mbuf *)0)
884 			break;
885 		m = m->m_next;
886 	}
887 	if (m->m_len > len) {
888 		m->m_len -= len;
889 		if (nul > 0) {
890 			cp = mtod(m, caddr_t)+m->m_len-nul;
891 			for (i = 0; i < nul; i++)
892 				*cp++ = '\0';
893 		}
894 		return;
895 	}
896 	count -= len;
897 	if (count < 0)
898 		count = 0;
899 	/*
900 	 * Correct length for chain is "count".
901 	 * Find the mbuf with last data, adjust its length,
902 	 * and toss data from remaining mbufs on chain.
903 	 */
904 	for (m = mp; m; m = m->m_next) {
905 		if (m->m_len >= count) {
906 			m->m_len = count;
907 			if (nul > 0) {
908 				cp = mtod(m, caddr_t)+m->m_len-nul;
909 				for (i = 0; i < nul; i++)
910 					*cp++ = '\0';
911 			}
912 			break;
913 		}
914 		count -= m->m_len;
915 	}
916 	while (m = m->m_next)
917 		m->m_len = 0;
918 }
919 
920 /*
921  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
922  * 	- look up fsid in mount list (if not found ret error)
923  *	- check that it is exported
924  *	- get vp by calling VFS_FHTOVP() macro
925  *	- if not lockflag unlock it with VOP_UNLOCK()
926  *	- if cred->cr_uid == 0 set it to m_exroot
927  */
928 nfsrv_fhtovp(fhp, lockflag, vpp, cred)
929 	fhandle_t *fhp;
930 	int lockflag;
931 	struct vnode **vpp;
932 	struct ucred *cred;
933 {
934 	register struct mount *mp;
935 
936 	if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
937 		return (ESTALE);
938 	if ((mp->m_flag & M_EXPORTED) == 0)
939 		return (EACCES);
940 	if (VFS_FHTOVP(mp, &fhp->fh_fid, vpp))
941 		return (ESTALE);
942 	if (cred->cr_uid == 0)
943 		cred->cr_uid = mp->m_exroot;
944 	if (!lockflag)
945 		VOP_UNLOCK(*vpp);
946 	return (0);
947 }
948