nfs_subs.c revision 84002
167754Smsmith/*
267754Smsmith * Copyright (c) 1989, 1993
367754Smsmith *	The Regents of the University of California.  All rights reserved.
4117521Snjl *
567754Smsmith * This code is derived from software contributed to Berkeley by
667754Smsmith * Rick Macklem at The University of Guelph.
767754Smsmith *
867754Smsmith * Redistribution and use in source and binary forms, with or without
967754Smsmith * modification, are permitted provided that the following conditions
1067754Smsmith * are met:
1167754Smsmith * 1. Redistributions of source code must retain the above copyright
12114237Snjl *    notice, this list of conditions and the following disclaimer.
1370243Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1467754Smsmith *    notice, this list of conditions and the following disclaimer in the
1567754Smsmith *    documentation and/or other materials provided with the distribution.
1667754Smsmith * 3. All advertising materials mentioning features or use of this software
1767754Smsmith *    must display the following acknowledgement:
1867754Smsmith *	This product includes software developed by the University of
1967754Smsmith *	California, Berkeley and its contributors.
2067754Smsmith * 4. Neither the name of the University nor the names of its contributors
2167754Smsmith *    may be used to endorse or promote products derived from this software
2267754Smsmith *    without specific prior written permission.
2367754Smsmith *
2467754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2567754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2667754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2767754Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2867754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2967754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3067754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3167754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3267754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3367754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3467754Smsmith * SUCH DAMAGE.
3567754Smsmith *
3667754Smsmith *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
3767754Smsmith */
3867754Smsmith
3967754Smsmith#include <sys/cdefs.h>
4067754Smsmith__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_subs.c 84002 2001-09-27 02:33:36Z peter $");
4167754Smsmith
4267754Smsmith/*
4367754Smsmith * These functions support the macros and help fiddle mbuf chains for
4467754Smsmith * the nfs op functions. They do things like create the rpc header and
4567754Smsmith * copy data between mbuf chains and uio lists.
4667754Smsmith */
4767754Smsmith
4867754Smsmith#include <sys/param.h>
4967754Smsmith#include <sys/systm.h>
5067754Smsmith#include <sys/kernel.h>
5167754Smsmith#include <sys/bio.h>
5267754Smsmith#include <sys/buf.h>
5367754Smsmith#include <sys/proc.h>
5467754Smsmith#include <sys/mount.h>
5567754Smsmith#include <sys/vnode.h>
5667754Smsmith#include <sys/namei.h>
5767754Smsmith#include <sys/mbuf.h>
5867754Smsmith#include <sys/socket.h>
5967754Smsmith#include <sys/stat.h>
6067754Smsmith#include <sys/malloc.h>
6167754Smsmith#include <sys/sysent.h>
6267754Smsmith#include <sys/syscall.h>
6367754Smsmith#include <sys/sysproto.h>
6467754Smsmith
6567754Smsmith#include <vm/vm.h>
6667754Smsmith#include <vm/vm_object.h>
6767754Smsmith#include <vm/vm_extern.h>
6867754Smsmith#include <vm/vm_zone.h>
6967754Smsmith
7067754Smsmith#include <nfs/rpcv2.h>
7167754Smsmith#include <nfs/nfsproto.h>
7267754Smsmith#include <nfsclient/nfs.h>
7367754Smsmith#include <nfsclient/nfsnode.h>
7467754Smsmith#include <nfs/xdr_subs.h>
7567754Smsmith#include <nfsclient/nfsm_subs.h>
7667754Smsmith#include <nfsclient/nfsmount.h>
7767754Smsmith
7867754Smsmith#include <netinet/in.h>
7967754Smsmith
8067754Smsmith/*
8167754Smsmith * Data items converted to xdr at startup, since they are constant
8267754Smsmith * This is kinda hokey, but may save a little time doing byte swaps
8367754Smsmith */
8467754Smsmithu_int32_t	nfs_xdrneg1;
8567754Smsmithu_int32_t	rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
8667754Smsmith		    rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
8767754Smsmithu_int32_t	nfs_true, nfs_false;
8867754Smsmith
8967754Smsmith/* And other global data */
9067754Smsmithstatic u_int32_t nfs_xid = 0;
9167754Smsmithstatic enum vtype nv2tov_type[8]= {
9267754Smsmith	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON,  VNON
9367754Smsmith};
9467754Smsmith
9567754Smsmithint		nfs_ticks;
9667754Smsmithint		nfs_pbuf_freecnt = -1;	/* start out unlimited */
9767754Smsmith
9867754Smsmithstruct nfs_reqq	nfs_reqq;
9967754Smsmithstruct nfs_bufq	nfs_bufq;
10067754Smsmith
10167754Smsmithstatic int	nfs_prev_nfsclnt_sy_narg;
10267754Smsmithstatic sy_call_t *nfs_prev_nfsclnt_sy_call;
10367754Smsmith
10467754Smsmith/*
10567754Smsmith * and the reverse mapping from generic to Version 2 procedure numbers
10667754Smsmith */
10767754Smsmithint nfsv2_procid[NFS_NPROCS] = {
10867754Smsmith	NFSV2PROC_NULL,
10967754Smsmith	NFSV2PROC_GETATTR,
11067754Smsmith	NFSV2PROC_SETATTR,
11167754Smsmith	NFSV2PROC_LOOKUP,
11267754Smsmith	NFSV2PROC_NOOP,
11367754Smsmith	NFSV2PROC_READLINK,
11467754Smsmith	NFSV2PROC_READ,
11567754Smsmith	NFSV2PROC_WRITE,
11667754Smsmith	NFSV2PROC_CREATE,
11767754Smsmith	NFSV2PROC_MKDIR,
11867754Smsmith	NFSV2PROC_SYMLINK,
11967754Smsmith	NFSV2PROC_CREATE,
12067754Smsmith	NFSV2PROC_REMOVE,
12167754Smsmith	NFSV2PROC_RMDIR,
12267754Smsmith	NFSV2PROC_RENAME,
12367754Smsmith	NFSV2PROC_LINK,
12467754Smsmith	NFSV2PROC_READDIR,
12567754Smsmith	NFSV2PROC_NOOP,
126102550Siwasaki	NFSV2PROC_STATFS,
12767754Smsmith	NFSV2PROC_NOOP,
128102550Siwasaki	NFSV2PROC_NOOP,
12991116Smsmith	NFSV2PROC_NOOP,
13067754Smsmith	NFSV2PROC_NOOP,
13167754Smsmith};
13267754Smsmith
13367754SmsmithLIST_HEAD(nfsnodehashhead, nfsnode);
134114237Snjl
135114237Snjl/*
136114237Snjl * Create the header for an rpc request packet
137114237Snjl * The hsiz is the size of the rest of the nfs request header.
138114237Snjl * (just used to decide if a cluster is a good idea)
139114237Snjl */
140114237Snjlstruct mbuf *
141114237Snjlnfsm_reqhead(struct vnode *vp, u_long procid, int hsiz)
142114237Snjl{
143114237Snjl	struct mbuf *mb;
144114237Snjl
145114237Snjl	MGET(mb, M_TRYWAIT, MT_DATA);
146114237Snjl	if (hsiz >= MINCLSIZE)
147114237Snjl		MCLGET(mb, M_TRYWAIT);
148114237Snjl	mb->m_len = 0;
149114237Snjl	return (mb);
150114237Snjl}
151114237Snjl
152114237Snjl/*
153114237Snjl * Build the RPC header and fill in the authorization info.
154114237Snjl * The authorization string argument is only used when the credentials
155114237Snjl * come from outside of the kernel.
156114237Snjl * Returns the head of the mbuf list.
157114237Snjl */
158114237Snjlstruct mbuf *
159114237Snjlnfsm_rpchead(struct ucred *cr, int nmflag, int procid, int auth_type,
160114237Snjl    int auth_len, struct mbuf *mrest, int mrest_len, struct mbuf **mbp,
161114237Snjl    u_int32_t *xidp)
162114237Snjl{
163114237Snjl	struct mbuf *mb;
164114237Snjl	u_int32_t *tl;
165114237Snjl	caddr_t bpos;
166114237Snjl	int i;
167114237Snjl	struct mbuf *mreq;
168114237Snjl	int grpsiz, authsiz;
169114237Snjl
170114237Snjl	authsiz = nfsm_rndup(auth_len);
171114237Snjl	MGETHDR(mb, M_TRYWAIT, MT_DATA);
172114237Snjl	if ((authsiz + 10 * NFSX_UNSIGNED) >= MINCLSIZE) {
173114237Snjl		MCLGET(mb, M_TRYWAIT);
17467754Smsmith	} else if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) {
17567754Smsmith		MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED);
17667754Smsmith	} else {
17767754Smsmith		MH_ALIGN(mb, 8 * NFSX_UNSIGNED);
17867754Smsmith	}
17967754Smsmith	mb->m_len = 0;
18067754Smsmith	mreq = mb;
18167754Smsmith	bpos = mtod(mb, caddr_t);
18267754Smsmith
18367754Smsmith	/*
18467754Smsmith	 * First the RPC header.
18567754Smsmith	 */
18667754Smsmith	tl = nfsm_build(u_int32_t *, 8 * NFSX_UNSIGNED);
18767754Smsmith
18867754Smsmith	/* Get a pretty random xid to start with */
18967754Smsmith	if (!nfs_xid)
19067754Smsmith		nfs_xid = random();
19167754Smsmith	/*
19291116Smsmith	 * Skip zero xid if it should ever happen.
19367754Smsmith	 */
19491116Smsmith	if (++nfs_xid == 0)
19567754Smsmith		nfs_xid++;
19667754Smsmith
19767754Smsmith	*tl++ = *xidp = txdr_unsigned(nfs_xid);
19867754Smsmith	*tl++ = rpc_call;
19967754Smsmith	*tl++ = rpc_vers;
20067754Smsmith	*tl++ = txdr_unsigned(NFS_PROG);
20167754Smsmith	if (nmflag & NFSMNT_NFSV3) {
20267754Smsmith		*tl++ = txdr_unsigned(NFS_VER3);
20367754Smsmith		*tl++ = txdr_unsigned(procid);
20467754Smsmith	} else {
20567754Smsmith		*tl++ = txdr_unsigned(NFS_VER2);
20667754Smsmith		*tl++ = txdr_unsigned(nfsv2_procid[procid]);
20767754Smsmith	}
20867754Smsmith
20967754Smsmith	/*
21067754Smsmith	 * And then the authorization cred.
21167754Smsmith	 */
21267754Smsmith	*tl++ = txdr_unsigned(auth_type);
21367754Smsmith	*tl = txdr_unsigned(authsiz);
21467754Smsmith	switch (auth_type) {
21567754Smsmith	case RPCAUTH_UNIX:
21667754Smsmith		tl = nfsm_build(u_int32_t *, auth_len);
21767754Smsmith		*tl++ = 0;		/* stamp ?? */
21867754Smsmith		*tl++ = 0;		/* NULL hostname */
21967754Smsmith		*tl++ = txdr_unsigned(cr->cr_uid);
22069746Smsmith		*tl++ = txdr_unsigned(cr->cr_groups[0]);
22167754Smsmith		grpsiz = (auth_len >> 2) - 5;
22282367Smsmith		*tl++ = txdr_unsigned(grpsiz);
223117521Snjl		for (i = 1; i <= grpsiz; i++)
224117521Snjl			*tl++ = txdr_unsigned(cr->cr_groups[i]);
22567754Smsmith		break;
22667754Smsmith	}
22767754Smsmith
22867754Smsmith	/*
22967754Smsmith	 * And the verifier...
23067754Smsmith	 */
23167754Smsmith	tl = nfsm_build(u_int32_t *, 2 * NFSX_UNSIGNED);
23267754Smsmith	*tl++ = txdr_unsigned(RPCAUTH_NULL);
23367754Smsmith	*tl = 0;
23467754Smsmith	mb->m_next = mrest;
23567754Smsmith	mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
23667754Smsmith	mreq->m_pkthdr.rcvif = (struct ifnet *)0;
23767754Smsmith	*mbp = mb;
23867754Smsmith	return (mreq);
23967754Smsmith}
24067754Smsmith
24167754Smsmith/*
24267754Smsmith * copies a uio scatter/gather list to an mbuf chain.
24367754Smsmith * NOTE: can ony handle iovcnt == 1
24467754Smsmith */
24567754Smsmithint
24667754Smsmithnfsm_uiotombuf(struct uio *uiop, struct mbuf **mq, int siz, caddr_t *bpos)
24767754Smsmith{
24867754Smsmith	char *uiocp;
24967754Smsmith	struct mbuf *mp, *mp2;
25067754Smsmith	int xfer, left, mlen;
25167754Smsmith	int uiosiz, clflg, rem;
25267754Smsmith	char *cp;
25367754Smsmith
25467754Smsmith#ifdef DIAGNOSTIC
25567754Smsmith	if (uiop->uio_iovcnt != 1)
25667754Smsmith		panic("nfsm_uiotombuf: iovcnt != 1");
25767754Smsmith#endif
25867754Smsmith
25967754Smsmith	if (siz > MLEN)		/* or should it >= MCLBYTES ?? */
26067754Smsmith		clflg = 1;
26167754Smsmith	else
26267754Smsmith		clflg = 0;
26367754Smsmith	rem = nfsm_rndup(siz)-siz;
26482367Smsmith	mp = mp2 = *mq;
26567754Smsmith	while (siz > 0) {
26667754Smsmith		left = uiop->uio_iov->iov_len;
26767754Smsmith		uiocp = uiop->uio_iov->iov_base;
26871867Smsmith		if (left > siz)
26982367Smsmith			left = siz;
27091116Smsmith		uiosiz = left;
27191116Smsmith		while (left > 0) {
27291116Smsmith			mlen = M_TRAILINGSPACE(mp);
27367754Smsmith			if (mlen == 0) {
27467754Smsmith				MGET(mp, M_TRYWAIT, MT_DATA);
27567754Smsmith				if (clflg)
27667754Smsmith					MCLGET(mp, M_TRYWAIT);
27767754Smsmith				mp->m_len = 0;
27867754Smsmith				mp2->m_next = mp;
27967754Smsmith				mp2 = mp;
28067754Smsmith				mlen = M_TRAILINGSPACE(mp);
28167754Smsmith			}
28267754Smsmith			xfer = (left > mlen) ? mlen : left;
28367754Smsmith#ifdef notdef
28467754Smsmith			/* Not Yet.. */
28567754Smsmith			if (uiop->uio_iov->iov_op != NULL)
28667754Smsmith				(*(uiop->uio_iov->iov_op))
28767754Smsmith				(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
28867754Smsmith			else
28999146Siwasaki#endif
290114237Snjl			if (uiop->uio_segflg == UIO_SYSSPACE)
291114237Snjl				bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
292117521Snjl			else
293117521Snjl				copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
294114237Snjl			mp->m_len += xfer;
295114237Snjl			left -= xfer;
296114237Snjl			uiocp += xfer;
297114237Snjl			uiop->uio_offset += xfer;
298114237Snjl			uiop->uio_resid -= xfer;
29967754Smsmith		}
30067754Smsmith		uiop->uio_iov->iov_base += uiosiz;
30167754Smsmith		uiop->uio_iov->iov_len -= uiosiz;
30267754Smsmith		siz -= uiosiz;
30367754Smsmith	}
304117521Snjl	if (rem > 0) {
305117521Snjl		if (rem > M_TRAILINGSPACE(mp)) {
30667754Smsmith			MGET(mp, M_TRYWAIT, MT_DATA);
30767754Smsmith			mp->m_len = 0;
30867754Smsmith			mp2->m_next = mp;
30967754Smsmith		}
31067754Smsmith		cp = mtod(mp, caddr_t)+mp->m_len;
31167754Smsmith		for (left = 0; left < rem; left++)
31267754Smsmith			*cp++ = '\0';
31367754Smsmith		mp->m_len += rem;
314107325Siwasaki		*bpos = cp;
31582367Smsmith	} else
31682367Smsmith		*bpos = mtod(mp, caddr_t)+mp->m_len;
31767754Smsmith	*mq = mp;
31867754Smsmith	return (0);
31982367Smsmith}
32067754Smsmith
32182367Smsmith/*
32267754Smsmith * Copy a string into mbufs for the hard cases...
32367754Smsmith */
32467754Smsmithint
32582367Smsmithnfsm_strtmbuf(struct mbuf **mb, char **bpos, const char *cp, long siz)
32667754Smsmith{
32782367Smsmith	struct mbuf *m1 = NULL, *m2;
32867754Smsmith	long left, xfer, len, tlen;
32967754Smsmith	u_int32_t *tl;
33067754Smsmith	int putsize;
33182367Smsmith
33267754Smsmith	putsize = 1;
33367754Smsmith	m2 = *mb;
33469746Smsmith	left = M_TRAILINGSPACE(m2);
33567754Smsmith	if (left > 0) {
33667754Smsmith		tl = ((u_int32_t *)(*bpos));
33767754Smsmith		*tl++ = txdr_unsigned(siz);
33867754Smsmith		putsize = 0;
33967754Smsmith		left -= NFSX_UNSIGNED;
34067754Smsmith		m2->m_len += NFSX_UNSIGNED;
34167754Smsmith		if (left > 0) {
34267754Smsmith			bcopy(cp, (caddr_t) tl, left);
34367754Smsmith			siz -= left;
34467754Smsmith			cp += left;
34567754Smsmith			m2->m_len += left;
34667754Smsmith			left = 0;
34767754Smsmith		}
34867754Smsmith	}
34967754Smsmith	/* Loop around adding mbufs */
35067754Smsmith	while (siz > 0) {
35167754Smsmith		MGET(m1, M_TRYWAIT, MT_DATA);
35267754Smsmith		if (siz > MLEN)
35367754Smsmith			MCLGET(m1, M_TRYWAIT);
354114237Snjl		m1->m_len = NFSMSIZ(m1);
35567754Smsmith		m2->m_next = m1;
35667754Smsmith		m2 = m1;
35767754Smsmith		tl = mtod(m1, u_int32_t *);
35867754Smsmith		tlen = 0;
35967754Smsmith		if (putsize) {
36067754Smsmith			*tl++ = txdr_unsigned(siz);
36167754Smsmith			m1->m_len -= NFSX_UNSIGNED;
36267754Smsmith			tlen = NFSX_UNSIGNED;
36391116Smsmith			putsize = 0;
36467754Smsmith		}
36567754Smsmith		if (siz < m1->m_len) {
36667754Smsmith			len = nfsm_rndup(siz);
36767754Smsmith			xfer = siz;
36867754Smsmith			if (xfer < len)
36967754Smsmith				*(tl+(xfer>>2)) = 0;
37067754Smsmith		} else {
37167754Smsmith			xfer = len = m1->m_len;
37267754Smsmith		}
37367754Smsmith		bcopy(cp, (caddr_t) tl, xfer);
37467754Smsmith		m1->m_len = len+tlen;
37567754Smsmith		siz -= xfer;
37667754Smsmith		cp += xfer;
37767754Smsmith	}
37867754Smsmith	*mb = m1;
37967754Smsmith	*bpos = mtod(m1, caddr_t)+m1->m_len;
38067754Smsmith	return (0);
38167754Smsmith}
38267754Smsmith
38367754Smsmith/*
38467754Smsmith * Called once to initialize data structures...
38567754Smsmith */
38667754Smsmithint
38767754Smsmithnfs_init(struct vfsconf *vfsp)
38867754Smsmith{
38967754Smsmith	int i;
39067754Smsmith
39167754Smsmith	nfsmount_zone = zinit("NFSMOUNT", sizeof(struct nfsmount), 0, 0, 1);
39267754Smsmith	rpc_vers = txdr_unsigned(RPC_VER2);
39367754Smsmith	rpc_call = txdr_unsigned(RPC_CALL);
39467754Smsmith	rpc_reply = txdr_unsigned(RPC_REPLY);
39567754Smsmith	rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
39667754Smsmith	rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
39767754Smsmith	rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
39867754Smsmith	rpc_autherr = txdr_unsigned(RPC_AUTHERR);
39967754Smsmith	rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
40067754Smsmith	nfs_true = txdr_unsigned(TRUE);
40167754Smsmith	nfs_false = txdr_unsigned(FALSE);
40267754Smsmith	nfs_xdrneg1 = txdr_unsigned(-1);
40367754Smsmith	nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
40467754Smsmith	if (nfs_ticks < 1)
40567754Smsmith		nfs_ticks = 1;
40667754Smsmith	/* Ensure async daemons disabled */
40767754Smsmith	for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
40867754Smsmith		nfs_iodwant[i] = (struct proc *)0;
40967754Smsmith		nfs_iodmount[i] = (struct nfsmount *)0;
41067754Smsmith	}
41167754Smsmith	nfs_nhinit();			/* Init the nfsnode table */
41299679Siwasaki
41367754Smsmith	/*
41467754Smsmith	 * Initialize reply list and start timer
41567754Smsmith	 */
41667754Smsmith	TAILQ_INIT(&nfs_reqq);
41784491Smsmith
41867754Smsmith	nfs_timer(0);
41967754Smsmith
42091116Smsmith	nfs_prev_nfsclnt_sy_narg = sysent[SYS_nfsclnt].sy_narg;
42184491Smsmith	sysent[SYS_nfsclnt].sy_narg = 2;
42284491Smsmith	nfs_prev_nfsclnt_sy_call = sysent[SYS_nfsclnt].sy_call;
42367754Smsmith	sysent[SYS_nfsclnt].sy_call = (sy_call_t *)nfsclnt;
42467754Smsmith
42567754Smsmith	nfs_pbuf_freecnt = nswbuf / 2 + 1;
42667754Smsmith
42799679Siwasaki	return (0);
42867754Smsmith}
42999679Siwasaki
43067754Smsmithint
43199679Siwasakinfs_uninit(struct vfsconf *vfsp)
43299679Siwasaki{
433117521Snjl
43484491Smsmith	untimeout(nfs_timer, (void *)NULL, nfs_timer_handle);
43584491Smsmith	sysent[SYS_nfsclnt].sy_narg = nfs_prev_nfsclnt_sy_narg;
43684491Smsmith	sysent[SYS_nfsclnt].sy_call = nfs_prev_nfsclnt_sy_call;
43784491Smsmith	return (0);
43867754Smsmith}
43999679Siwasaki
44084491Smsmith/*
44184491Smsmith * Attribute cache routines.
44299679Siwasaki * nfs_loadattrcache() - loads or updates the cache contents from attributes
44384491Smsmith *	that are on the mbuf list
44499679Siwasaki * nfs_getattrcache() - returns valid attributes if found in cache, returns
44584491Smsmith *	error otherwise
44684491Smsmith */
44784491Smsmith
44884491Smsmith/*
44984491Smsmith * Load the attribute cache (that lives in the nfsnode entry) with
45099679Siwasaki * the values on the mbuf list and
45184491Smsmith * Iff vap not NULL
45284491Smsmith *    copy the attributes to *vaper
45384491Smsmith */
45499679Siwasakiint
45599679Siwasakinfs_loadattrcache(struct vnode **vpp, struct mbuf **mdp, caddr_t *dposp,
45667754Smsmith    struct vattr *vaper, int dontshrink)
45767754Smsmith{
45867754Smsmith	struct vnode *vp = *vpp;
45967754Smsmith	struct vattr *vap;
46099679Siwasaki	struct nfs_fattr *fp;
46167754Smsmith	struct nfsnode *np;
46267754Smsmith	int32_t t1;
46367754Smsmith	caddr_t cp2;
46467754Smsmith	int error = 0, rdev;
46599679Siwasaki	struct mbuf *md;
46667754Smsmith	enum vtype vtyp;
46767754Smsmith	u_short vmode;
46867754Smsmith	struct timespec mtime;
46967754Smsmith	int v3 = NFS_ISV3(vp);
47067754Smsmith
47167754Smsmith	md = *mdp;
47267754Smsmith	t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
47367754Smsmith	if ((error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2)) != 0)
47467754Smsmith		return (error);
47567754Smsmith	fp = (struct nfs_fattr *)cp2;
47667754Smsmith	if (v3) {
47784491Smsmith		vtyp = nfsv3tov_type(fp->fa_type);
47867754Smsmith		vmode = fxdr_unsigned(u_short, fp->fa_mode);
47967754Smsmith		rdev = makeudev(fxdr_unsigned(int, fp->fa3_rdev.specdata1),
48067754Smsmith			fxdr_unsigned(int, fp->fa3_rdev.specdata2));
48167754Smsmith		fxdr_nfsv3time(&fp->fa3_mtime, &mtime);
48267754Smsmith	} else {
48367754Smsmith		vtyp = nfsv2tov_type(fp->fa_type);
48467754Smsmith		vmode = fxdr_unsigned(u_short, fp->fa_mode);
48567754Smsmith		/*
48667754Smsmith		 * XXX
48767754Smsmith		 *
48867754Smsmith		 * The duplicate information returned in fa_type and fa_mode
48967754Smsmith		 * is an ambiguity in the NFS version 2 protocol.
49067754Smsmith		 *
49167754Smsmith		 * VREG should be taken literally as a regular file.  If a
49267754Smsmith		 * server intents to return some type information differently
49367754Smsmith		 * in the upper bits of the mode field (e.g. for sockets, or
49467754Smsmith		 * FIFOs), NFSv2 mandates fa_type to be VNON.  Anyway, we
49567754Smsmith		 * leave the examination of the mode bits even in the VREG
49667754Smsmith		 * case to avoid breakage for bogus servers, but we make sure
49791116Smsmith		 * that there are actually type bits set in the upper part of
49891116Smsmith		 * fa_mode (and failing that, trust the va_type field).
49991116Smsmith		 *
50067754Smsmith		 * NFSv3 cleared the issue, and requires fa_mode to not
50167754Smsmith		 * contain any type information (while also introduing sockets
50267754Smsmith		 * and FIFOs for fa_type).
50367754Smsmith		 */
504114237Snjl		if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0))
50567754Smsmith			vtyp = IFTOVT(vmode);
506114237Snjl		rdev = fxdr_unsigned(int32_t, fp->fa2_rdev);
50767754Smsmith		fxdr_nfsv2time(&fp->fa2_mtime, &mtime);
50867754Smsmith
50967754Smsmith		/*
51067754Smsmith		 * Really ugly NFSv2 kludge.
51167754Smsmith		 */
51267754Smsmith		if (vtyp == VCHR && rdev == 0xffffffff)
51367754Smsmith			vtyp = VFIFO;
51467754Smsmith	}
51567754Smsmith
51667754Smsmith	/*
51767754Smsmith	 * If v_type == VNON it is a new node, so fill in the v_type,
51867754Smsmith	 * n_mtime fields. Check to see if it represents a special
51967754Smsmith	 * device, and if so, check for a possible alias. Once the
52067754Smsmith	 * correct vnode has been obtained, fill in the rest of the
52167754Smsmith	 * information.
52291116Smsmith	 */
52391116Smsmith	np = VTONFS(vp);
52491116Smsmith	if (vp->v_type != vtyp) {
52591116Smsmith		vp->v_type = vtyp;
52691116Smsmith		if (vp->v_type == VFIFO) {
527117521Snjl			vp->v_op = fifo_nfsv2nodeop_p;
52867754Smsmith		}
52967754Smsmith		if (vp->v_type == VCHR || vp->v_type == VBLK) {
530117521Snjl			vp->v_op = spec_nfsv2nodeop_p;
531117521Snjl			vp = addaliasu(vp, rdev);
53267754Smsmith			np->n_vnode = vp;
53367754Smsmith		}
53480062Smsmith		np->n_mtime = mtime.tv_sec;
53567754Smsmith	}
53667754Smsmith	vap = &np->n_vattr;
53767754Smsmith	vap->va_type = vtyp;
53867754Smsmith	vap->va_mode = (vmode & 07777);
539102550Siwasaki	vap->va_rdev = rdev;
54067754Smsmith	vap->va_mtime = mtime;
54167754Smsmith	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
542	if (v3) {
543		vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
544		vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
545		vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
546		vap->va_size = fxdr_hyper(&fp->fa3_size);
547		vap->va_blocksize = NFS_FABLKSIZE;
548		vap->va_bytes = fxdr_hyper(&fp->fa3_used);
549		vap->va_fileid = fxdr_unsigned(int32_t,
550		    fp->fa3_fileid.nfsuquad[1]);
551		fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime);
552		fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime);
553		vap->va_flags = 0;
554		vap->va_filerev = 0;
555	} else {
556		vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
557		vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
558		vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
559		vap->va_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
560		vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
561		vap->va_bytes = (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks)
562		    * NFS_FABLKSIZE;
563		vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
564		fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
565		vap->va_flags = 0;
566		vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t,
567		    fp->fa2_ctime.nfsv2_sec);
568		vap->va_ctime.tv_nsec = 0;
569		vap->va_gen = fxdr_unsigned(u_int32_t, fp->fa2_ctime.nfsv2_usec);
570		vap->va_filerev = 0;
571	}
572	np->n_attrstamp = time_second;
573	if (vap->va_size != np->n_size) {
574		if (vap->va_type == VREG) {
575			if (dontshrink && vap->va_size < np->n_size) {
576				/*
577				 * We've been told not to shrink the file;
578				 * zero np->n_attrstamp to indicate that
579				 * the attributes are stale.
580				 */
581				vap->va_size = np->n_size;
582				np->n_attrstamp = 0;
583			} else if (np->n_flag & NMODIFIED) {
584				if (vap->va_size < np->n_size)
585					vap->va_size = np->n_size;
586				else
587					np->n_size = vap->va_size;
588			} else {
589				np->n_size = vap->va_size;
590			}
591			vnode_pager_setsize(vp, np->n_size);
592		} else {
593			np->n_size = vap->va_size;
594		}
595	}
596	if (vaper != NULL) {
597		bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
598		if (np->n_flag & NCHG) {
599			if (np->n_flag & NACC)
600				vaper->va_atime = np->n_atim;
601			if (np->n_flag & NUPD)
602				vaper->va_mtime = np->n_mtim;
603		}
604	}
605	return (0);
606}
607
608#ifdef NFS_ACDEBUG
609#include <sys/sysctl.h>
610SYSCTL_DECL(_vfs_nfs);
611static int nfs_acdebug;
612SYSCTL_INT(_vfs_nfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
613#endif
614
615/*
616 * Check the time stamp
617 * If the cache is valid, copy contents to *vap and return 0
618 * otherwise return an error
619 */
620int
621nfs_getattrcache(struct vnode *vp, struct vattr *vaper)
622{
623	struct nfsnode *np;
624	struct vattr *vap;
625	struct nfsmount *nmp;
626	int timeo;
627
628	np = VTONFS(vp);
629	vap = &np->n_vattr;
630	nmp = VFSTONFS(vp->v_mount);
631	/* XXX n_mtime doesn't seem to be updated on a miss-and-reload */
632	timeo = (time_second - np->n_mtime) / 10;
633
634#ifdef NFS_ACDEBUG
635	if (nfs_acdebug>1)
636		printf("nfs_getattrcache: initial timeo = %d\n", timeo);
637#endif
638
639	if (vap->va_type == VDIR) {
640		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin)
641			timeo = nmp->nm_acdirmin;
642		else if (timeo > nmp->nm_acdirmax)
643			timeo = nmp->nm_acdirmax;
644	} else {
645		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin)
646			timeo = nmp->nm_acregmin;
647		else if (timeo > nmp->nm_acregmax)
648			timeo = nmp->nm_acregmax;
649	}
650
651#ifdef NFS_ACDEBUG
652	if (nfs_acdebug > 2)
653		printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
654			nmp->nm_acregmin, nmp->nm_acregmax,
655			nmp->nm_acdirmin, nmp->nm_acdirmax);
656
657	if (nfs_acdebug)
658		printf("nfs_getattrcache: age = %d; final timeo = %d\n",
659			(time_second - np->n_attrstamp), timeo);
660#endif
661
662	if ((time_second - np->n_attrstamp) >= timeo) {
663		nfsstats.attrcache_misses++;
664		return (ENOENT);
665	}
666	nfsstats.attrcache_hits++;
667	if (vap->va_size != np->n_size) {
668		if (vap->va_type == VREG) {
669			if (np->n_flag & NMODIFIED) {
670				if (vap->va_size < np->n_size)
671					vap->va_size = np->n_size;
672				else
673					np->n_size = vap->va_size;
674			} else {
675				np->n_size = vap->va_size;
676			}
677			vnode_pager_setsize(vp, np->n_size);
678		} else {
679			np->n_size = vap->va_size;
680		}
681	}
682	bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
683	if (np->n_flag & NCHG) {
684		if (np->n_flag & NACC)
685			vaper->va_atime = np->n_atim;
686		if (np->n_flag & NUPD)
687			vaper->va_mtime = np->n_mtim;
688	}
689	return (0);
690}
691
692static nfsuint64 nfs_nullcookie = { { 0, 0 } };
693/*
694 * This function finds the directory cookie that corresponds to the
695 * logical byte offset given.
696 */
697nfsuint64 *
698nfs_getcookie(struct nfsnode *np, off_t off, int add)
699{
700	struct nfsdmap *dp, *dp2;
701	int pos;
702
703	pos = (uoff_t)off / NFS_DIRBLKSIZ;
704	if (pos == 0 || off < 0) {
705#ifdef DIAGNOSTIC
706		if (add)
707			panic("nfs getcookie add at <= 0");
708#endif
709		return (&nfs_nullcookie);
710	}
711	pos--;
712	dp = LIST_FIRST(&np->n_cookies);
713	if (!dp) {
714		if (add) {
715			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
716				M_NFSDIROFF, M_WAITOK);
717			dp->ndm_eocookie = 0;
718			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
719		} else
720			return ((nfsuint64 *)0);
721	}
722	while (pos >= NFSNUMCOOKIES) {
723		pos -= NFSNUMCOOKIES;
724		if (LIST_NEXT(dp, ndm_list)) {
725			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
726				pos >= dp->ndm_eocookie)
727				return ((nfsuint64 *)0);
728			dp = LIST_NEXT(dp, ndm_list);
729		} else if (add) {
730			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
731				M_NFSDIROFF, M_WAITOK);
732			dp2->ndm_eocookie = 0;
733			LIST_INSERT_AFTER(dp, dp2, ndm_list);
734			dp = dp2;
735		} else
736			return ((nfsuint64 *)0);
737	}
738	if (pos >= dp->ndm_eocookie) {
739		if (add)
740			dp->ndm_eocookie = pos + 1;
741		else
742			return ((nfsuint64 *)0);
743	}
744	return (&dp->ndm_cookies[pos]);
745}
746
747/*
748 * Invalidate cached directory information, except for the actual directory
749 * blocks (which are invalidated separately).
750 * Done mainly to avoid the use of stale offset cookies.
751 */
752void
753nfs_invaldir(struct vnode *vp)
754{
755	struct nfsnode *np = VTONFS(vp);
756
757#ifdef DIAGNOSTIC
758	if (vp->v_type != VDIR)
759		panic("nfs: invaldir not dir");
760#endif
761	np->n_direofoffset = 0;
762	np->n_cookieverf.nfsuquad[0] = 0;
763	np->n_cookieverf.nfsuquad[1] = 0;
764	if (LIST_FIRST(&np->n_cookies))
765		LIST_FIRST(&np->n_cookies)->ndm_eocookie = 0;
766}
767
768/*
769 * The write verifier has changed (probably due to a server reboot), so all
770 * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
771 * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
772 * and B_CLUSTEROK flags.  Once done the new write verifier can be set for the
773 * mount point.
774 *
775 * B_CLUSTEROK must be cleared along with B_NEEDCOMMIT because stage 1 data
776 * writes are not clusterable.
777 */
778void
779nfs_clearcommit(struct mount *mp)
780{
781	struct vnode *vp, *nvp;
782	struct buf *bp, *nbp;
783	int s;
784
785	GIANT_REQUIRED;
786
787	s = splbio();
788	mtx_lock(&mntvnode_mtx);
789loop:
790	for (vp = LIST_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) {
791		if (vp->v_mount != mp)	/* Paranoia */
792			goto loop;
793		nvp = LIST_NEXT(vp, v_mntvnodes);
794		for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
795			nbp = TAILQ_NEXT(bp, b_vnbufs);
796			if (BUF_REFCNT(bp) == 0 &&
797			    (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
798				== (B_DELWRI | B_NEEDCOMMIT))
799				bp->b_flags &= ~(B_NEEDCOMMIT | B_CLUSTEROK);
800		}
801	}
802	mtx_unlock(&mntvnode_mtx);
803	splx(s);
804}
805
806/*
807 * Helper functions for former macros.  Some of these should be
808 * moved to their callers.
809 */
810
811int
812nfsm_mtofh_xx(struct vnode *d, struct vnode **v, int v3, int *f,
813    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
814{
815	struct nfsnode *ttnp;
816	struct vnode *ttvp;
817	nfsfh_t *ttfhp;
818	int ttfhsize;
819	int t1;
820
821	if (v3) {
822		t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
823		if (t1)
824			return (t1);
825		*f = fxdr_unsigned(int, **tl);
826	} else
827		*f = 1;
828	if (*f) {
829		t1 = nfsm_getfh_xx(&ttfhp, &ttfhsize, (v3), tl, md, dpos);
830		if (t1 != 0)
831			return t1;
832		t1 = nfs_nget(d->v_mount, ttfhp, ttfhsize, &ttnp);
833		if (t1 != 0)
834			return t1;
835		*v = NFSTOV(ttnp);
836	}
837	if (v3) {
838		t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
839		if (t1)
840			return t1;
841		if (*f)
842			*f = fxdr_unsigned(int, **tl);
843		else if (fxdr_unsigned(int, **tl))
844			nfsm_adv_xx(NFSX_V3FATTR, tl, md, dpos);
845	}
846	if (*f) {
847		ttvp = *v;
848		t1 = nfs_loadattrcache(&ttvp, md, dpos, (struct vattr *)0, 0);
849		if (t1)
850			return t1;
851		*v = ttvp;
852	}
853	return 0;
854}
855
856int
857nfsm_getfh_xx(nfsfh_t **f, int *s, int v3,
858    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
859{
860	int t1;
861
862	if (v3) {
863		t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
864		if (t1)
865			return t1;
866		*s = fxdr_unsigned(int, **tl);
867		if (*s <= 0 || *s > NFSX_V3FHMAX) {
868			return EBADRPC;
869		}
870	} else
871		*s = NFSX_V2FH;
872	t1 = nfsm_dissect_xx((void **)f, nfsm_rndup(*s), md, dpos);
873	return t1;
874}
875
876
877int
878nfsm_loadattr_xx(struct vnode **v, struct vattr *va,
879    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
880{
881	int t1;
882
883	struct vnode *ttvp = *v;
884	t1 = nfs_loadattrcache(&ttvp, md, dpos, va, 0);
885	if (t1 != 0)
886		return t1;
887	*v = ttvp;
888	return 0;
889}
890
891int
892nfsm_postop_attr_xx(struct vnode **v, int *f,
893    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
894{
895	int t1;
896
897	struct vnode *ttvp = *v;
898	t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
899	if (t1 != 0)
900		return t1;
901	*f = fxdr_unsigned(int, **tl);
902	if (*f != 0) {
903		t1 = nfs_loadattrcache(&ttvp, md, dpos, (struct vattr *)0, 1);
904		if (t1 != 0) {
905			*f = 0;
906			return t1;
907		}
908		*v = ttvp;
909	}
910	return 0;
911}
912
913int
914nfsm_wcc_data_xx(struct vnode **v, int *f,
915    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
916{
917	int ttattrf, ttretf = 0;
918	int t1;
919
920	t1 = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
921	if (t1 != 0)
922		return t1;
923	if (**tl == nfs_true) {
924		t1 = nfsm_dissect_xx((void **)tl, 6 * NFSX_UNSIGNED, md, dpos);
925		if (t1 != 0)
926			return t1;
927		if (*f)
928			ttretf = (VTONFS(*v)->n_mtime ==
929			    fxdr_unsigned(u_int32_t, *((*tl) + 2)));
930	}
931	t1 = nfsm_postop_attr_xx(v, &ttattrf, tl, md, dpos);
932	if (t1)
933		return t1;
934	if (*f)
935		*f = ttretf;
936	else
937		*f = ttattrf;
938	return 0;
939}
940
941int
942nfsm_strtom_xx(const char *a, int s, int m,
943    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
944{
945	int t1;
946
947	if (s > m)
948		return ENAMETOOLONG;
949	t1 = nfsm_rndup(s) + NFSX_UNSIGNED;
950	if (t1 <= M_TRAILINGSPACE(*mb)) {
951		*tl = nfsm_build_xx(t1, mb, bpos);
952		*(*tl)++ = txdr_unsigned(s);
953		*((*tl) + ((t1 >> 2) - 2)) = 0;
954		bcopy(a, *tl, s);
955	} else {
956		t1 = nfsm_strtmbuf(mb, bpos, a, s);
957		if (t1 != 0)
958			return t1;
959	}
960	return 0;
961}
962
963int
964nfsm_fhtom_xx(struct vnode *v, int v3,
965    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
966{
967	int t1;
968	caddr_t cp;
969
970	if (v3) {
971		t1 = nfsm_rndup(VTONFS(v)->n_fhsize) + NFSX_UNSIGNED;
972		if (t1 < M_TRAILINGSPACE(*mb)) {
973			*tl = nfsm_build_xx(t1, mb, bpos);
974			*(*tl)++ = txdr_unsigned(VTONFS(v)->n_fhsize);
975			*((*tl) + ((t1 >> 2) - 2)) = 0;
976			bcopy(VTONFS(v)->n_fhp, *tl, VTONFS(v)->n_fhsize);
977		} else {
978			t1 = nfsm_strtmbuf(mb, bpos,
979			    (const char *)VTONFS(v)->n_fhp,
980			    VTONFS(v)->n_fhsize);
981			if (t1 != 0)
982				return t1;
983		}
984	} else {
985		cp = nfsm_build_xx(NFSX_V2FH, mb, bpos);
986		bcopy(VTONFS(v)->n_fhp, cp, NFSX_V2FH);
987	}
988	return 0;
989}
990
991void
992nfsm_v3attrbuild_xx(struct vattr *va, int full,
993    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
994{
995
996	if (va->va_mode != (mode_t)VNOVAL) {
997		*tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos);
998		*(*tl)++ = nfs_true;
999		**tl = txdr_unsigned(va->va_mode);
1000	} else {
1001		*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1002		**tl = nfs_false;
1003	}
1004	if (full && va->va_uid != (uid_t)VNOVAL) {
1005		*tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos);
1006		*(*tl)++ = nfs_true;
1007		**tl = txdr_unsigned(va->va_uid);
1008	} else {
1009		*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1010		**tl = nfs_false;
1011	}
1012	if (full && va->va_gid != (gid_t)VNOVAL) {
1013		*tl = nfsm_build_xx(2 * NFSX_UNSIGNED, mb, bpos);
1014		*(*tl)++ = nfs_true;
1015		**tl = txdr_unsigned(va->va_gid);
1016	} else {
1017		*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1018		**tl = nfs_false;
1019	}
1020	if (full && va->va_size != VNOVAL) {
1021		*tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos);
1022		*(*tl)++ = nfs_true;
1023		txdr_hyper(va->va_size, *tl);
1024	} else {
1025		*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1026		**tl = nfs_false;
1027	}
1028	if (va->va_atime.tv_sec != VNOVAL) {
1029		if (va->va_atime.tv_sec != time_second) {
1030			*tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos);
1031			*(*tl)++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
1032			txdr_nfsv3time(&va->va_atime, *tl);
1033		} else {
1034			*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1035			**tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
1036		}
1037	} else {
1038		*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1039		**tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
1040	}
1041	if (va->va_mtime.tv_sec != VNOVAL) {
1042		if (va->va_mtime.tv_sec != time_second) {
1043			*tl = nfsm_build_xx(3 * NFSX_UNSIGNED, mb, bpos);
1044			*(*tl)++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
1045			txdr_nfsv3time(&va->va_mtime, *tl);
1046		} else {
1047			*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1048			**tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
1049		}
1050	} else {
1051		*tl = nfsm_build_xx(NFSX_UNSIGNED, mb, bpos);
1052		**tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
1053	}
1054}
1055