nfsm_subs.h revision 1.41
1/*	$NetBSD: nfsm_subs.h,v 1.41 2005/05/29 20:58:13 christos Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Rick Macklem at The University of Guelph.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	@(#)nfsm_subs.h	8.2 (Berkeley) 3/30/95
35 */
36
37
38#ifndef _NFS_NFSM_SUBS_H_
39#define _NFS_NFSM_SUBS_H_
40
41
42/*
43 * These macros do strange and peculiar things to mbuf chains for
44 * the assistance of the nfs code. To attempt to use them for any
45 * other purpose will be dangerous. (they make weird assumptions)
46 */
47
48/*
49 * First define what the actual subs. return
50 */
51
52#define	M_HASCL(m)	((m)->m_flags & M_EXT)
53#define	NFSMADV(m, s)	(m)->m_data += (s)
54#define	NFSMSIZ(m)	((M_HASCL(m)) ? (m)->m_ext.ext_size : \
55				(((m)->m_flags & M_PKTHDR) ? MHLEN : MLEN))
56
57/*
58 * Now for the macros that do the simple stuff and call the functions
59 * for the hard stuff.
60 * These macros use several vars. declared in nfsm_reqhead and these
61 * vars. must not be used elsewhere unless you are careful not to corrupt
62 * them. The vars. starting with pN and tN (N=1,2,3,..) are temporaries
63 * that may be used so long as the value is not expected to retained
64 * after a macro.
65 * I know, this is kind of dorkey, but it makes the actual op functions
66 * fairly clean and deals with the mess caused by the xdr discriminating
67 * unions.
68 */
69
70#define	nfsm_build(a,c,s) \
71		{ if ((s) > M_TRAILINGSPACE(mb)) { \
72			struct mbuf *mb2; \
73			mb2 = m_get(M_WAIT, MT_DATA); \
74			MCLAIM(mb2, &nfs_mowner); \
75			if ((s) > MLEN) \
76				panic("build > MLEN"); \
77			mb->m_next = mb2; \
78			mb = mb2; \
79			mb->m_len = 0; \
80			bpos = mtod(mb, caddr_t); \
81		} \
82		(a) = (c)(bpos); \
83		mb->m_len += (s); \
84		bpos += (s); }
85
86#define nfsm_aligned(p) ALIGNED_POINTER(p,u_int32_t)
87
88#define	nfsm_dissect(a, c, s) \
89		{ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
90		if (t1 >= (s) && nfsm_aligned(dpos)) { \
91			(a) = (c)(dpos); \
92			dpos += (s); \
93		} else if ((t1 = nfsm_disct(&md, &dpos, (s), t1, &cp2)) != 0){ \
94			error = t1; \
95			m_freem(mrep); \
96			goto nfsmout; \
97		} else { \
98			(a) = (c)cp2; \
99		} }
100
101#define nfsm_fhtom(n, v3) \
102	      { if (v3) { \
103			t2 = nfsm_rndup((n)->n_fhsize) + NFSX_UNSIGNED; \
104			if (t2 <= M_TRAILINGSPACE(mb)) { \
105				nfsm_build(tl, u_int32_t *, t2); \
106				*tl++ = txdr_unsigned((n)->n_fhsize); \
107				*(tl + ((t2>>2) - 2)) = 0; \
108				memcpy((caddr_t)tl,(caddr_t)(n)->n_fhp, \
109					(n)->n_fhsize); \
110			} else if ((t2 = nfsm_strtmbuf(&mb, &bpos, \
111				(caddr_t)(n)->n_fhp, \
112				  (n)->n_fhsize)) != 0) { \
113				error = t2; \
114				m_freem(mreq); \
115				goto nfsmout; \
116			} \
117		} else { \
118			nfsm_build(cp, caddr_t, NFSX_V2FH); \
119			memcpy(cp, (caddr_t)(n)->n_fhp, NFSX_V2FH); \
120		} }
121
122#define nfsm_srvfhtom(f, v3) \
123		{ if (v3) { \
124			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_V3FH); \
125			*tl++ = txdr_unsigned(NFSX_V3FH); \
126			memcpy((caddr_t)tl, (caddr_t)(f), NFSX_V3FH); \
127		} else { \
128			nfsm_build(cp, caddr_t, NFSX_V2FH); \
129			memcpy(cp, (caddr_t)(f), NFSX_V2FH); \
130		} }
131
132#define nfsm_srvpostop_fh(f) \
133		{ nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED + NFSX_V3FH); \
134		*tl++ = nfs_true; \
135		*tl++ = txdr_unsigned(NFSX_V3FH); \
136		memcpy((caddr_t)tl, (caddr_t)(f), NFSX_V3FH); \
137		}
138
139/*
140 * nfsm_mtofh: dissect a "resulted obj" part of create-like operations
141 * like mkdir.
142 *
143 * for nfsv3, dissect post_op_fh3 and following post_op_attr.
144 * for nfsv2, dissect fhandle and following fattr.
145 *
146 * d: (IN) the vnode of the parent directry.
147 * v: (OUT) the corresponding vnode (we allocate one if needed)
148 * v3: (IN) true for nfsv3.
149 * f: (OUT) true if we got valid filehandle.  always true for nfsv2.
150 */
151
152#define nfsm_mtofh(d, v, v3, f) \
153		{ struct nfsnode *ttnp; nfsfh_t *ttfhp; int ttfhsize; \
154		int hasattr = 0; \
155		if (v3) { \
156			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
157			(f) = fxdr_unsigned(int, *tl); \
158		} else { \
159			(f) = 1; \
160			hasattr = 1; \
161		} \
162		if (f) { \
163			nfsm_getfh(ttfhp, ttfhsize, (v3)); \
164			if ((t1 = nfs_nget((d)->v_mount, ttfhp, ttfhsize, \
165				&ttnp)) != 0) { \
166				error = t1; \
167				m_freem(mrep); \
168				goto nfsmout; \
169			} \
170			(v) = NFSTOV(ttnp); \
171		} \
172		if (v3) { \
173			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
174			if (f) \
175				hasattr = fxdr_unsigned(int, *tl); \
176			else if (fxdr_unsigned(int, *tl)) \
177				nfsm_adv(NFSX_V3FATTR); \
178		} \
179		if (f && hasattr) \
180			nfsm_loadattr((v), (struct vattr *)0, 0); \
181		}
182
183/*
184 * nfsm_getfh: dissect a filehandle.
185 *
186 * f: (OUT) a filehandle.
187 * s: (OUT) size of the filehandle in bytes.
188 * v3: (IN) true if nfsv3.
189 */
190
191#define nfsm_getfh(f, s, v3) \
192		{ if (v3) { \
193			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
194			if (((s) = fxdr_unsigned(int, *tl)) <= 0 || \
195				(s) > NFSX_V3FHMAX) { \
196				m_freem(mrep); \
197				error = EBADRPC; \
198				goto nfsmout; \
199			} \
200		} else \
201			(s) = NFSX_V2FH; \
202		nfsm_dissect((f), nfsfh_t *, nfsm_rndup(s)); }
203
204#define	nfsm_loadattr(v, a, flags) \
205		{ struct vnode *ttvp = (v); \
206		if ((t1 = nfsm_loadattrcache(&ttvp, &md, &dpos, (a), (flags))) \
207		    != 0) { \
208			error = t1; \
209			m_freem(mrep); \
210			goto nfsmout; \
211		} \
212		(v) = ttvp; }
213
214/*
215 * nfsm_postop_attr: process nfsv3 post_op_attr
216 *
217 * dissect post_op_attr.  if we got a one,
218 * call nfsm_loadattrcache to update attribute cache.
219 *
220 * v: (IN/OUT) the corresponding vnode
221 * f: (OUT) true if we got valid attribute
222 * flags: (IN) flags for nfsm_loadattrcache
223 */
224
225#define	nfsm_postop_attr(v, f, flags) \
226		{ struct vnode *ttvp = (v); \
227		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
228		if (((f) = fxdr_unsigned(int, *tl)) != 0) { \
229			if ((t1 = nfsm_loadattrcache(&ttvp, &md, &dpos, \
230				(struct vattr *)0, (flags))) != 0) { \
231				error = t1; \
232				(f) = 0; \
233				m_freem(mrep); \
234				goto nfsmout; \
235			} \
236			(v) = ttvp; \
237		} }
238
239/*
240 * nfsm_wcc_data: process nfsv3 wcc_data
241 *
242 * dissect pre_op_attr and then let nfsm_postop_attr dissect post_op_attr.
243 *
244 * v: (IN/OUT) the corresponding vnode
245 * f: (IN/OUT)
246 *	NFSV3_WCCRATTR	return true if we got valid post_op_attr.
247 *	NFSV3_WCCCHK	return true if pre_op_attr's mtime is the same
248 *			as our n_mtime.  (ie. our cache isn't stale.)
249 * flags: (IN) flags for nfsm_loadattrcache
250 * docheck: (IN) TRUE if timestamp change is expected
251 */
252
253/* Used as (f) for nfsm_wcc_data() */
254#define NFSV3_WCCRATTR	0
255#define NFSV3_WCCCHK	1
256
257#define	nfsm_wcc_data(v, f, flags, docheck) \
258		{ int ttattrf, ttretf = 0, renewctime = 0, renewnctime = 0; \
259		struct timespec ctime, mtime; \
260		struct nfsnode *nfsp = VTONFS(v); \
261		boolean_t haspreopattr = FALSE; \
262		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
263		if (*tl == nfs_true) { \
264			haspreopattr = TRUE; \
265			nfsm_dissect(tl, u_int32_t *, 6 * NFSX_UNSIGNED); \
266			fxdr_nfsv3time(tl + 2, &mtime); \
267			fxdr_nfsv3time(tl + 4, &ctime); \
268			if (nfsp->n_ctime == ctime.tv_sec) \
269				renewctime = 1; \
270			if ((v)->v_type == VDIR) { \
271				if (timespeccmp(&nfsp->n_nctime, &ctime, ==)) \
272					renewnctime = 1; \
273			} \
274			if (f) { \
275				ttretf = timespeccmp(&nfsp->n_mtime, &mtime, ==);\
276			} \
277		} \
278		nfsm_postop_attr((v), ttattrf, (flags)); \
279		nfsp = VTONFS(v); \
280		if (ttattrf) { \
281			if (haspreopattr && \
282			    nfs_check_wccdata(nfsp, &ctime, &mtime, (docheck))) \
283				renewctime = renewnctime = ttretf = 0; \
284			if (renewctime) \
285				nfsp->n_ctime = nfsp->n_vattr->va_ctime.tv_sec; \
286			if (renewnctime) \
287				nfsp->n_nctime = nfsp->n_vattr->va_ctime; \
288		} \
289		if (f) { \
290			(f) = ttretf; \
291		} else { \
292			(f) = ttattrf; \
293		} }
294
295/* If full is true, set all fields, otherwise just set mode and time fields */
296#define nfsm_v3attrbuild(a, full)						\
297		{ if ((a)->va_mode != (mode_t)VNOVAL) {				\
298			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);		\
299			*tl++ = nfs_true;					\
300			*tl = txdr_unsigned((a)->va_mode);			\
301		} else {							\
302			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);		\
303			*tl = nfs_false;					\
304		}								\
305		if ((full) && (a)->va_uid != (uid_t)VNOVAL) {			\
306			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);		\
307			*tl++ = nfs_true;					\
308			*tl = txdr_unsigned((a)->va_uid);			\
309		} else {							\
310			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);		\
311			*tl = nfs_false;					\
312		}								\
313		if ((full) && (a)->va_gid != (gid_t)VNOVAL) {			\
314			nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);		\
315			*tl++ = nfs_true;					\
316			*tl = txdr_unsigned((a)->va_gid);			\
317		} else {							\
318			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);		\
319			*tl = nfs_false;					\
320		}								\
321		if ((full) && (a)->va_size != VNOVAL) {				\
322			nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);		\
323			*tl++ = nfs_true;					\
324			txdr_hyper((a)->va_size, tl);				\
325		} else {							\
326			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);		\
327			*tl = nfs_false;					\
328		}								\
329		if ((a)->va_atime.tv_sec != VNOVAL) {				\
330			if ((a)->va_atime.tv_sec != time.tv_sec) {		\
331				nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);	\
332				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);	\
333				txdr_nfsv3time(&(a)->va_atime, tl);		\
334			} else {						\
335				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);	\
336				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);	\
337			}							\
338		} else {							\
339			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);		\
340			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);		\
341		}								\
342		if ((a)->va_mtime.tv_sec != VNOVAL) {				\
343			if ((a)->va_mtime.tv_sec != time.tv_sec) {		\
344				nfsm_build(tl, u_int32_t *, 3 * NFSX_UNSIGNED);	\
345				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);	\
346				txdr_nfsv3time(&(a)->va_mtime, tl);		\
347			} else {						\
348				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);	\
349				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);	\
350			}							\
351		} else {							\
352			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);		\
353			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);		\
354		}								\
355		}
356
357
358#define	nfsm_strsiz(s,m) \
359		{ nfsm_dissect(tl,uint32_t *,NFSX_UNSIGNED); \
360		if (((s) = fxdr_unsigned(uint32_t,*tl)) > (m)) { \
361			m_freem(mrep); \
362			error = EBADRPC; \
363			goto nfsmout; \
364		} }
365
366#define	nfsm_srvnamesiz(s) \
367		{ nfsm_dissect(tl,uint32_t *,NFSX_UNSIGNED); \
368		if (((s) = fxdr_unsigned(uint32_t,*tl)) > NFS_MAXNAMLEN) \
369			error = NFSERR_NAMETOL; \
370		if (error) \
371			nfsm_reply(0); \
372		}
373
374#define nfsm_mtouio(p,s) \
375		if ((s) > 0 && \
376		   (t1 = nfsm_mbuftouio(&md,(p),(s),&dpos)) != 0) { \
377			error = t1; \
378			m_freem(mrep); \
379			goto nfsmout; \
380		}
381
382#define nfsm_uiotom(p,s) \
383		if ((t1 = nfsm_uiotombuf((p),&mb,(s),&bpos)) != 0) { \
384			error = t1; \
385			m_freem(mreq); \
386			goto nfsmout; \
387		}
388
389#define	nfsm_reqhead(n,a,s) \
390		mb = mreq = nfsm_reqh((n),(a),(s),&bpos)
391
392#define nfsm_reqdone	m_freem(mrep); \
393		nfsmout:
394
395#define nfsm_rndup(a)	(((a)+3)&(~0x3))
396#define nfsm_padlen(a)	(nfsm_rndup(a) - (a))
397
398#define	nfsm_request1(v, t, p, c, rexmitp)	\
399		if ((error = nfs_request((v), mreq, (t), (p), \
400		   (c), &mrep, &md, &dpos, (rexmitp))) != 0) { \
401			if (error & NFSERR_RETERR) \
402				error &= ~NFSERR_RETERR; \
403			else \
404				goto nfsmout; \
405		}
406
407#define	nfsm_request(v, t, p, c)	nfsm_request1((v), (t), (p), (c), NULL)
408
409#define	nfsm_strtom(a,s,m) \
410		if ((s) > (m)) { \
411			m_freem(mreq); \
412			error = ENAMETOOLONG; \
413			goto nfsmout; \
414		} \
415		t2 = nfsm_rndup(s)+NFSX_UNSIGNED; \
416		if (t2 <= M_TRAILINGSPACE(mb)) { \
417			nfsm_build(tl,u_int32_t *,t2); \
418			*tl++ = txdr_unsigned(s); \
419			*(tl+((t2>>2)-2)) = 0; \
420			memcpy((caddr_t)tl, (const char *)(a), (s)); \
421		} else if ((t2 = nfsm_strtmbuf(&mb, &bpos, (a), (s))) != 0) { \
422			error = t2; \
423			m_freem(mreq); \
424			goto nfsmout; \
425		}
426
427#define	nfsm_srvdone \
428		nfsmout: \
429		return(error)
430
431#define	nfsm_reply(s) \
432		{ \
433		nfsd->nd_repstat = error; \
434		if (error && !(nfsd->nd_flag & ND_NFSV3)) \
435		   (void) nfs_rephead(0, nfsd, slp, error, cache, &frev, \
436			mrq, &mb, &bpos); \
437		else \
438		   (void) nfs_rephead((s), nfsd, slp, error, cache, &frev, \
439			mrq, &mb, &bpos); \
440		if (mrep != NULL) { \
441			m_freem(mrep); \
442			mrep = NULL; \
443		} \
444		mreq = *mrq; \
445		if (error && (!(nfsd->nd_flag & ND_NFSV3) || \
446			error == EBADRPC)) \
447			return(0); \
448		}
449
450#define	nfsm_writereply(s, v3) \
451		{ \
452		nfsd->nd_repstat = error; \
453		if (error && !(v3)) \
454		   (void) nfs_rephead(0, nfsd, slp, error, cache, &frev, \
455			&mreq, &mb, &bpos); \
456		else \
457		   (void) nfs_rephead((s), nfsd, slp, error, cache, &frev, \
458			&mreq, &mb, &bpos); \
459		}
460
461#define	nfsm_adv(s) \
462		{ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
463		if (t1 >= (s)) { \
464			dpos += (s); \
465		} else if ((t1 = nfs_adv(&md, &dpos, (s), t1)) != 0) { \
466			error = t1; \
467			m_freem(mrep); \
468			goto nfsmout; \
469		} }
470
471#define nfsm_srvmtofh(f) \
472	{ int fhlen = NFSX_V3FH; \
473		if (nfsd->nd_flag & ND_NFSV3) { \
474			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
475			fhlen = fxdr_unsigned(int, *tl); \
476			if (fhlen == 0) { \
477				memset((caddr_t)(f), 0, NFSX_V3FH); \
478			} else if (fhlen != NFSX_V3FH) { \
479				error = EBADRPC; \
480				nfsm_reply(0); \
481			} \
482		} \
483		if (fhlen != 0) { \
484			nfsm_dissect(tl, u_int32_t *, NFSX_V3FH); \
485			memcpy( (caddr_t)(f), (caddr_t)tl, NFSX_V3FH); \
486			if ((nfsd->nd_flag & ND_NFSV3) == 0) \
487				nfsm_adv(NFSX_V2FH - NFSX_V3FH); \
488		} \
489	}
490
491#define	nfsm_clget \
492		if (bp >= be) { \
493			if (mp == mb) \
494				mp->m_len += bp-bpos; \
495			mp = m_get(M_WAIT, MT_DATA); \
496			MCLAIM(mp, &nfs_mowner); \
497			m_clget(mp, M_WAIT); \
498			mp->m_len = NFSMSIZ(mp); \
499			mp2->m_next = mp; \
500			mp2 = mp; \
501			bp = mtod(mp, caddr_t); \
502			be = bp+mp->m_len; \
503		} \
504		tl = (u_int32_t *)bp
505
506#define	nfsm_srvfillattr(a, f) \
507		nfsm_srvfattr(nfsd, (a), (f))
508
509#define nfsm_srvwcc_data(br, b, ar, a) \
510		nfsm_srvwcc(nfsd, (br), (b), (ar), (a), &mb, &bpos)
511
512#define nfsm_srvpostop_attr(r, a) \
513		nfsm_srvpostopattr(nfsd, (r), (a), &mb, &bpos)
514
515#define nfsm_srvsattr(a) \
516		{ nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
517		if (*tl == nfs_true) { \
518			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
519			(a)->va_mode = nfstov_mode(*tl); \
520		} \
521		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
522		if (*tl == nfs_true) { \
523			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
524			(a)->va_uid = fxdr_unsigned(uid_t, *tl); \
525		} \
526		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
527		if (*tl == nfs_true) { \
528			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
529			(a)->va_gid = fxdr_unsigned(gid_t, *tl); \
530		} \
531		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
532		if (*tl == nfs_true) { \
533			nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED); \
534			(a)->va_size = fxdr_hyper(tl); \
535		} \
536		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
537		switch (fxdr_unsigned(int, *tl)) { \
538		case NFSV3SATTRTIME_TOCLIENT: \
539			nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED); \
540			fxdr_nfsv3time(tl, &(a)->va_atime); \
541			break; \
542		case NFSV3SATTRTIME_TOSERVER: \
543			(a)->va_atime.tv_sec = time.tv_sec; \
544			(a)->va_atime.tv_nsec = time.tv_usec * 1000; \
545			(a)->va_vaflags |= VA_UTIMES_NULL; \
546			break; \
547		}; \
548		nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); \
549		switch (fxdr_unsigned(int, *tl)) { \
550		case NFSV3SATTRTIME_TOCLIENT: \
551			nfsm_dissect(tl, u_int32_t *, 2 * NFSX_UNSIGNED); \
552			fxdr_nfsv3time(tl, &(a)->va_mtime); \
553			(a)->va_vaflags &= ~VA_UTIMES_NULL; \
554			break; \
555		case NFSV3SATTRTIME_TOSERVER: \
556			(a)->va_mtime.tv_sec = time.tv_sec; \
557			(a)->va_mtime.tv_nsec = time.tv_usec * 1000; \
558			(a)->va_vaflags |= VA_UTIMES_NULL; \
559			break; \
560		}; }
561
562#endif
563