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