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