xref: /csrg-svn/sys/nfs/nfs_subs.c (revision 38737)
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.6 (Berkeley) 08/22/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 "rpcv2.h"
44 #include "nfsv2.h"
45 #include "nfsnode.h"
46 #include "nfs.h"
47 #include "xdr_subs.h"
48 #include "nfsm_subs.h"
49 
50 #define TRUE	1
51 #define	FALSE	0
52 
53 /*
54  * Data items converted to xdr at startup, since they are constant
55  * This is kinda hokey, but may save a little time doing byte swaps
56  */
57 u_long nfs_procids[NFS_NPROCS];
58 u_long nfs_xdrneg1;
59 u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied,
60 	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
61 u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
62 /* And other global data */
63 static u_long *rpc_uidp = (u_long *)0;
64 static u_long nfs_xid = 1;
65 static char *rpc_unixauth;
66 extern long hostid;
67 extern enum vtype v_type[NFLNK+1];
68 
69 /* Function ret types */
70 static char *nfs_unixauth();
71 
72 /*
73  * Maximum number of groups passed through to NFS server.
74  * For release 3.X systems, the maximum value is 8.
75  * For release 4.X systems, the maximum value is 10.
76  */
77 int numgrps = 8;
78 
79 /*
80  * Create the header for an rpc request packet
81  * The function nfs_unixauth() creates a unix style authorization string
82  * and returns a ptr to it.
83  * The hsiz is the size of the rest of the nfs request header.
84  * (just used to decide if a cluster is a good idea)
85  * nb: Note that the prog, vers and proc args are already in xdr byte order
86  */
87 struct mbuf *nfsm_reqh(prog, vers, proc, cred, hsiz, bpos, mb, retxid)
88 	u_long prog;
89 	u_long vers;
90 	u_long proc;
91 	struct ucred *cred;
92 	int hsiz;
93 	caddr_t *bpos;
94 	struct mbuf **mb;
95 	u_long *retxid;
96 {
97 	register struct mbuf *mreq, *m;
98 	register u_long *p;
99 	struct mbuf *m1;
100 	char *ap;
101 	int asiz, siz;
102 
103 	NFSMGETHDR(mreq);
104 	asiz = (((cred->cr_ngroups > numgrps) ? numgrps : cred->cr_ngroups)<<2);
105 #ifdef FILLINHOST
106 	asiz += nfsm_rndup(hostnamelen)+(9*NFSX_UNSIGNED);
107 #else
108 	asiz += 9*NFSX_UNSIGNED;
109 #endif
110 
111 	/* If we need a lot, alloc a cluster ?? */
112 	if ((asiz+hsiz+RPC_SIZ) > MHLEN)
113 		NFSMCLGET(mreq, M_WAIT);
114 	mreq->m_len = NFSMSIZ(mreq);
115 	siz = mreq->m_len;
116 	m1 = mreq;
117 	/*
118 	 * Alloc enough mbufs
119 	 * We do it now to avoid all sleeps after the call to nfs_unixauth()
120 	 */
121 	while ((asiz+RPC_SIZ) > siz) {
122 		MGET(m, M_WAIT, MT_DATA);
123 		m1->m_next = m;
124 		m->m_len = MLEN;
125 		siz += MLEN;
126 		m1 = m;
127 	}
128 	p = mtod(mreq, u_long *);
129 	*p++ = *retxid = txdr_unsigned(++nfs_xid);
130 	*p++ = rpc_call;
131 	*p++ = rpc_vers;
132 	*p++ = prog;
133 	*p++ = vers;
134 	*p++ = proc;
135 
136 	/* Now we can call nfs_unixauth() and copy it in */
137 	ap = nfs_unixauth(cred);
138 	m = mreq;
139 	siz = m->m_len-RPC_SIZ;
140 	if (asiz <= siz) {
141 		bcopy(ap, (caddr_t)p, asiz);
142 		m->m_len = asiz+RPC_SIZ;
143 	} else {
144 		bcopy(ap, (caddr_t)p, siz);
145 		ap += siz;
146 		asiz -= siz;
147 		while (asiz > 0) {
148 			siz = (asiz > MLEN) ? MLEN : asiz;
149 			m = m->m_next;
150 			bcopy(ap, mtod(m, caddr_t), siz);
151 			m->m_len = siz;
152 			asiz -= siz;
153 			ap += siz;
154 		}
155 	}
156 
157 	/* Finally, return values */
158 	*mb = m;
159 	*bpos = mtod(m, caddr_t)+m->m_len;
160 	return (mreq);
161 }
162 
163 /*
164  * copies mbuf chain to the uio scatter/gather list
165  */
166 nfsm_mbuftouio(mrep, uiop, siz, dpos)
167 	struct mbuf **mrep;
168 	struct uio *uiop;
169 	int siz;
170 	caddr_t *dpos;
171 {
172 	register int xfer, left, len;
173 	register struct mbuf *mp;
174 	register char *mbufcp, *uiocp;
175 	long uiosiz, rem;
176 
177 	mp = *mrep;
178 	mbufcp = *dpos;
179 	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
180 	rem = nfsm_rndup(siz)-siz;
181 	while (siz > 0) {
182 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
183 			return(EFBIG);
184 		left = uiop->uio_iov->iov_len;
185 		uiocp = uiop->uio_iov->iov_base;
186 		if (left > siz)
187 			left = siz;
188 		uiosiz = left;
189 		while (left > 0) {
190 			while (len == 0) {
191 				mp = mp->m_next;
192 				if (mp == NULL)
193 					return (EBADRPC);
194 				mbufcp = mtod(mp, caddr_t);
195 				len = mp->m_len;
196 			}
197 			xfer = (left > len) ? len : left;
198 #ifdef notdef
199 			/* Not Yet.. */
200 			if (uiop->uio_iov->iov_op != NULL)
201 				(*(uiop->uio_iov->iov_op))
202 				(mbufcp, uiocp, xfer);
203 			else
204 #endif
205 			if (uiop->uio_segflg == UIO_SYSSPACE)
206 				bcopy(mbufcp, uiocp, xfer);
207 			else
208 				copyout(mbufcp, uiocp, xfer);
209 			left -= xfer;
210 			len -= xfer;
211 			mbufcp += xfer;
212 			uiocp += 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, off;
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_resid -= xfer;
285 		}
286 		if (uiop->uio_iov->iov_len <= siz) {
287 			uiop->uio_iovcnt--;
288 			uiop->uio_iov++;
289 		} else {
290 			uiop->uio_iov->iov_base += uiosiz;
291 			uiop->uio_iov->iov_len -= uiosiz;
292 		}
293 		siz -= uiosiz;
294 	}
295 	if (rem > 0) {
296 		if (rem > (len-mp->m_len)) {
297 			MGET(mp, M_WAIT, MT_DATA);
298 			mp->m_len = 0;
299 			mp2->m_next = mp;
300 		}
301 		cp = mtod(mp, caddr_t)+mp->m_len;
302 		for (left = 0; left < rem; left++)
303 			*cp++ = '\0';
304 		mp->m_len += rem;
305 		*bpos = cp;
306 	} else
307 		*bpos = mtod(mp, caddr_t)+mp->m_len;
308 	*mq = mp;
309 	return(0);
310 }
311 
312 /*
313  * Help break down an mbuf chain by setting the first siz bytes contiguous
314  * pointed to by returned val.
315  * If Updateflg == True we can overwrite the first part of the mbuf data
316  * This is used by the macros nfsm_disect and nfsm_disecton for tough
317  * cases. (The macros use the vars. dpos and dpos2)
318  */
319 nfsm_disct(mdp, dposp, siz, left, updateflg, cp2)
320 	struct mbuf **mdp;
321 	caddr_t *dposp;
322 	int siz;
323 	int left;
324 	int updateflg;
325 	caddr_t *cp2;
326 {
327 	register struct mbuf *mp, *mp2;
328 	register int siz2, xfer;
329 	register caddr_t p;
330 	caddr_t p2;
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 		return (0);
386 	}
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 nfsinit()
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 	v_type[0] = VNON;
502 	v_type[1] = VREG;
503 	v_type[2] = VDIR;
504 	v_type[3] = VBLK;
505 	v_type[4] = VCHR;
506 	v_type[5] = VLNK;
507 	nfs_xdrneg1 = txdr_unsigned(-1);
508 	nfs_nhinit();			/* Init the nfsnode table */
509 	/* And start timer */
510 	nfs_timer();
511 }
512 
513 /*
514  * Fill in the rest of the rpc_unixauth and return it
515  */
516 static char *nfs_unixauth(cr)
517 	register struct ucred *cr;
518 {
519 	register u_long *p;
520 	register int i;
521 	int ngr;
522 
523 	/* Maybe someday there should be a cache of AUTH_SHORT's */
524 	if ((p = rpc_uidp) == NULL) {
525 #ifdef FILLINHOST
526 		i = nfsm_rndup(hostnamelen)+(19*NFSX_UNSIGNED);
527 #else
528 		i = 19*NFSX_UNSIGNED;
529 #endif
530 		MALLOC(p, u_long *, i, M_TEMP, M_WAITOK);
531 		bzero((caddr_t)p, i);
532 		rpc_unixauth = (caddr_t)p;
533 		*p++ = txdr_unsigned(RPCAUTH_UNIX);
534 		p++;	/* Fill in size later */
535 		*p++ = hostid;
536 #ifdef FILLINHOST
537 		*p++ = txdr_unsigned(hostnamelen);
538 		i = nfsm_rndup(hostnamelen);
539 		bcopy(hostname, (caddr_t)p, hostnamelen);
540 		p += (i>>2);
541 #else
542 		*p++ = 0;
543 #endif
544 		rpc_uidp = p;
545 	}
546 	*p++ = txdr_unsigned(cr->cr_uid);
547 	*p++ = txdr_unsigned(cr->cr_groups[0]);
548 	ngr = (cr->cr_ngroups > numgrps) ? numgrps : cr->cr_ngroups;
549 	*p++ = txdr_unsigned(ngr);
550 	for (i = 0; i < ngr; i++)
551 		*p++ = txdr_unsigned(cr->cr_groups[i]);
552 	/* And add the AUTH_NULL */
553 	*p++ = 0;
554 	*p = 0;
555 	i = (((caddr_t)p)-rpc_unixauth)-12;
556 	p = (u_long *)(rpc_unixauth+4);
557 	*p = txdr_unsigned(i);
558 	return(rpc_unixauth);
559 }
560 
561 /*
562  * Attribute cache routines.
563  * nfs_loadattrcache() - loads or updates the cache contents from attributes
564  *	that are on the mbuf list
565  * nfs_getattrcache() - returns valid attributes if found in cache, returns
566  *	error otherwise
567  */
568 
569 /*
570  * Load the attribute cache (that lives in the nfsnode table) with
571  * the values on the mbuf list and
572  * Iff vap not NULL
573  *    copy the attributes to *vaper
574  */
575 nfs_loadattrcache(vp, mdp, dposp, vaper)
576 	register struct vnode *vp;
577 	struct mbuf **mdp;
578 	caddr_t *dposp;
579 	struct vattr *vaper;
580 {
581 	register struct vattr *vap;
582 	nfsm_vars;
583 	struct nfsnode *np;
584 
585 	md = *mdp;
586 	dpos = *dposp;
587 	t1 = (mtod(md, caddr_t)+md->m_len)-dpos;
588 	if (error = nfsm_disct(&md, &dpos, NFSX_FATTR, t1, TRUE, &cp2))
589 		return (error);
590 	p = (u_long *)cp2;
591 	np = VTONFS(vp);
592 	vap = &np->n_vattr;
593 	vap->va_type = nfstov_type(*p++);
594 	vap->va_mode = nfstov_mode(*p++);
595 	vap->va_nlink = fxdr_unsigned(u_short, *p++);
596 	vap->va_uid = fxdr_unsigned(uid_t, *p++);
597 	vap->va_gid = fxdr_unsigned(gid_t, *p++);
598 	vap->va_size = fxdr_unsigned(u_long, *p++);
599 	vap->va_size1 = 0;		/* OR -1 ?? */
600 	vap->va_blocksize = fxdr_unsigned(long, *p++);
601 	vap->va_rdev = fxdr_unsigned(dev_t, *p++);
602 	vap->va_bytes = fxdr_unsigned(long, *p++) * vap->va_blocksize;
603 	vap->va_bytes1 = -1;
604 	vap->va_fsid = fxdr_unsigned(long, *p++);
605 	vap->va_fileid = fxdr_unsigned(long, *p++);
606 	fxdr_time(p, &(vap->va_atime));
607 	p += 2;
608 	fxdr_time(p, &(vap->va_mtime));
609 	p += 2;
610 	fxdr_time(p, &(vap->va_ctime));
611 	vap->va_gen = 0;
612 	vap->va_flags = 0;
613 	np->n_attrstamp = time.tv_sec;
614 	*dposp = dpos;
615 	*mdp = md;
616 	if (vaper != NULL)
617 		bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
618 	return (0);
619 }
620 
621 /*
622  * Check the time stamp
623  * If the cache is valid, copy contents to *vap and return 0
624  * otherwise return an error
625  */
626 nfs_getattrcache(vp, vap)
627 	register struct vnode *vp;
628 	struct vattr *vap;
629 {
630 	register struct nfsnode *np;
631 
632 	np = VTONFS(vp);
633 	if ((time.tv_sec-np->n_attrstamp) < NFS_ATTRTIMEO) {
634 		nfsstats.attrcache_hits++;
635 		bcopy((caddr_t)&np->n_vattr,(caddr_t)vap,sizeof(struct vattr));
636 		return (0);
637 	} else {
638 		nfsstats.attrcache_misses++;
639 		return (ENOENT);
640 	}
641 }
642 
643 /*
644  * nfs_namei - a liitle like namei(), but for one element only
645  *	essentially look up file handle, fill in ndp and call VOP_LOOKUP()
646  */
647 nfs_namei(ndp, fhp, len, mdp, dposp)
648 	register struct nameidata *ndp;
649 	fhandle_t *fhp;
650 	int len;
651 	struct mbuf **mdp;
652 	caddr_t *dposp;
653 {
654 	register int i, rem;
655 	register struct mbuf *md;
656 	register char *cp;
657 	struct vnode *dp = (struct vnode *)0;
658 	struct vnode *tdp;
659 	struct mount *mp;
660 	int flag;
661 	int docache;
662 	int wantparent;
663 	int lockparent;
664 	int rootflg = 0;
665 	int error = 0;
666 
667 	ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0;
668 	flag = ndp->ni_nameiop & OPFLAG;
669 	wantparent = ndp->ni_nameiop & (LOCKPARENT | WANTPARENT);
670 	lockparent = ndp->ni_nameiop & LOCKPARENT;
671 	docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE;
672 	if (flag == DELETE || wantparent)
673 		docache = 0;
674 
675 	/* Fill in the nameidata and call lookup */
676 	cp = *dposp;
677 	md = *mdp;
678 	rem = mtod(md, caddr_t)+md->m_len-cp;
679 	ndp->ni_hash = 0;
680 	for (i = 0; i < len;) {
681 		if (rem == 0) {
682 			md = md->m_next;
683 			if (md == NULL)
684 				return (EBADRPC);
685 			cp = mtod(md, caddr_t);
686 			rem = md->m_len;
687 		}
688 		if (*cp == '\0' || *cp == '/')
689 			return (EINVAL);
690 		if (*cp & 0200)
691 			if ((*cp&0377) == ('/'|0200) || flag != DELETE)
692 				return (EINVAL);
693 		ndp->ni_dent.d_name[i++] = *cp;
694 		ndp->ni_hash += (unsigned char)*cp * i;
695 		cp++;
696 		rem--;
697 	}
698 	*mdp = md;
699 	len = nfsm_rndup(len)-len;
700 	if (len > 0)
701 		*dposp = cp+len;
702 	else
703 		*dposp = cp;
704 	ndp->ni_namelen = i;
705 	ndp->ni_dent.d_namlen = i;
706 	ndp->ni_dent.d_name[i] = '\0';
707 	ndp->ni_pathlen = 1;
708 	ndp->ni_dirp = ndp->ni_ptr = &ndp->ni_dent.d_name[0];
709 	ndp->ni_next = &ndp->ni_dent.d_name[i];
710 	ndp->ni_loopcnt = 0;	/* Not actually used for now */
711 	ndp->ni_endoff = 0;
712 	if (docache)
713 		ndp->ni_makeentry = 1;
714 	else
715 		ndp->ni_makeentry = 0;
716 	ndp->ni_isdotdot = (i == 2 &&
717 		ndp->ni_dent.d_name[1] == '.' && ndp->ni_dent.d_name[0] == '.');
718 
719 	if (error = nfsrv_fhtovp(fhp, TRUE, &dp, ndp->ni_cred))
720 		return (error);
721 	if (dp->v_type != VDIR) {
722 		vput(dp);
723 		return (ENOTDIR);
724 	}
725 	ndp->ni_cdir = dp;
726 	ndp->ni_rdir = (struct vnode *)0;
727 
728 	/*
729 	 * Handle "..":
730 	 * If this vnode is the root of the mounted
731 	 *    file system, then ignore it so can't get out
732 	 */
733 	if (ndp->ni_isdotdot && (dp->v_flag & VROOT)) {
734 		ndp->ni_dvp = dp;
735 		ndp->ni_vp = dp;
736 		VREF(dp);
737 		goto nextname;
738 	}
739 
740 	/*
741 	 * We now have a segment name to search for, and a directory to search.
742 	 */
743 	if (error = VOP_LOOKUP(dp, ndp)) {
744 		if (ndp->ni_vp != NULL)
745 			panic("leaf should be empty");
746 		/*
747 		 * If creating and at end of pathname, then can consider
748 		 * allowing file to be created.
749 		 */
750 		if (ndp->ni_dvp->v_mount->m_flag & (M_RDONLY | M_EXRDONLY))
751 			error = EROFS;
752 		if (flag == LOOKUP || flag == DELETE || error != ENOENT)
753 			goto bad;
754 		/*
755 		 * We return with ni_vp NULL to indicate that the entry
756 		 * doesn't currently exist, leaving a pointer to the
757 		 * (possibly locked) directory inode in ndp->ni_dvp.
758 		 */
759 		return (0);	/* should this be ENOENT? */
760 	}
761 
762 	dp = ndp->ni_vp;
763 
764 nextname:
765 	ndp->ni_ptr = ndp->ni_next;
766 	/*
767 	 * Check for read-only file systems
768 	 */
769 	if (flag == DELETE || flag == RENAME) {
770 		/*
771 		 * Disallow directory write attempts on read-only
772 		 * file systems.
773 		 */
774 		if ((dp->v_mount->m_flag & (M_RDONLY|M_EXRDONLY)) ||
775 		    (wantparent && (ndp->ni_dvp->v_mount->m_flag & (M_RDONLY|M_EXRDONLY)))) {
776 			error = EROFS;
777 			goto bad2;
778 		}
779 	}
780 
781 	if (!wantparent)
782 		vrele(ndp->ni_dvp);
783 
784 	if ((ndp->ni_nameiop & LOCKLEAF) == 0)
785 		VOP_UNLOCK(dp);
786 	return (0);
787 
788 bad2:
789 	if (lockparent)
790 		VOP_UNLOCK(ndp->ni_dvp);
791 	vrele(ndp->ni_dvp);
792 bad:
793 	vput(dp);
794 	ndp->ni_vp = NULL;
795 	return (error);
796 }
797 
798 /*
799  * A fiddled version of m_adj() that ensures null fill to a long
800  * boundary and only trims off the back end
801  */
802 nfsm_adj(mp, len, nul)
803 	struct mbuf *mp;
804 	register int len;
805 	int nul;
806 {
807 	register struct mbuf *m;
808 	register int count, i;
809 	register char *cp;
810 
811 	/*
812 	 * Trim from tail.  Scan the mbuf chain,
813 	 * calculating its length and finding the last mbuf.
814 	 * If the adjustment only affects this mbuf, then just
815 	 * adjust and return.  Otherwise, rescan and truncate
816 	 * after the remaining size.
817 	 */
818 	count = 0;
819 	m = mp;
820 	for (;;) {
821 		count += m->m_len;
822 		if (m->m_next == (struct mbuf *)0)
823 			break;
824 		m = m->m_next;
825 	}
826 	if (m->m_len > len) {
827 		m->m_len -= len;
828 		if (nul > 0) {
829 			cp = mtod(m, caddr_t)+m->m_len-nul;
830 			for (i = 0; i < nul; i++)
831 				*cp++ = '\0';
832 		}
833 		return;
834 	}
835 	count -= len;
836 	if (count < 0)
837 		count = 0;
838 	/*
839 	 * Correct length for chain is "count".
840 	 * Find the mbuf with last data, adjust its length,
841 	 * and toss data from remaining mbufs on chain.
842 	 */
843 	for (m = mp; m; m = m->m_next) {
844 		if (m->m_len >= count) {
845 			m->m_len = count;
846 			if (nul > 0) {
847 				cp = mtod(m, caddr_t)+m->m_len-nul;
848 				for (i = 0; i < nul; i++)
849 					*cp++ = '\0';
850 			}
851 			break;
852 		}
853 		count -= m->m_len;
854 	}
855 	while (m = m->m_next)
856 		m->m_len = 0;
857 }
858 
859 /*
860  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
861  * 	- look up fsid in mount list (if not found ret error)
862  *	- check that it is exported
863  *	- get vp by calling VFS_FHTOVP() macro
864  *	- if not lockflag unlock it with VOP_UNLOCK()
865  *	- if cred->cr_uid == 0 set it to m_exroot
866  */
867 nfsrv_fhtovp(fhp, lockflag, vpp, cred)
868 	fhandle_t *fhp;
869 	int lockflag;
870 	struct vnode **vpp;
871 	struct ucred *cred;
872 {
873 	register struct mount *mp;
874 	int error;
875 
876 	if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
877 		return (ESTALE);
878 	if ((mp->m_flag & M_EXPORTED) == 0)
879 		return (EACCES);
880 	if (VFS_FHTOVP(mp, &fhp->fh_fid, vpp))
881 		return (ESTALE);
882 	if (cred->cr_uid == 0)
883 		cred->cr_uid = mp->m_exroot;
884 	if (!lockflag)
885 		VOP_UNLOCK(*vpp);
886 	return (0);
887 }
888 
889