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