nfs_srvsubs.c revision 111119
1/*
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  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, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
37 * $FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 111119 2003-02-19 05:47:46Z imp $
38 */
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 111119 2003-02-19 05:47:46Z imp $");
42
43/*
44 * These functions support the macros and help fiddle mbuf chains for
45 * the nfs op functions. They do things like create the rpc header and
46 * copy data between mbuf chains and uio lists.
47 */
48
49#include "opt_inet6.h"
50
51#include <sys/param.h>
52#include <sys/systm.h>
53#include <sys/kernel.h>
54#include <sys/bio.h>
55#include <sys/buf.h>
56#include <sys/proc.h>
57#include <sys/mount.h>
58#include <sys/vnode.h>
59#include <sys/namei.h>
60#include <sys/mbuf.h>
61#include <sys/socket.h>
62#include <sys/stat.h>
63#include <sys/malloc.h>
64#include <sys/module.h>
65#include <sys/sysent.h>
66#include <sys/syscall.h>
67#include <sys/sysproto.h>
68
69#include <vm/vm.h>
70#include <vm/vm_object.h>
71#include <vm/vm_extern.h>
72#include <vm/uma.h>
73
74#include <nfs/rpcv2.h>
75#include <nfs/nfsproto.h>
76#include <nfsserver/nfs.h>
77#include <nfs/xdr_subs.h>
78#include <nfsserver/nfsm_subs.h>
79
80#include <netinet/in.h>
81
82/*
83 * Data items converted to xdr at startup, since they are constant
84 * This is kinda hokey, but may save a little time doing byte swaps
85 */
86u_int32_t nfsrv_nfs_xdrneg1;
87u_int32_t nfsrv_rpc_call, nfsrv_rpc_vers, nfsrv_rpc_reply,
88	nfsrv_rpc_msgdenied, nfsrv_rpc_autherr,
89	nfsrv_rpc_mismatch, nfsrv_rpc_auth_unix, nfsrv_rpc_msgaccepted;
90u_int32_t nfsrv_nfs_prog, nfsrv_nfs_true, nfsrv_nfs_false;
91
92/* And other global data */
93static nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK,
94				 NFNON, NFCHR, NFNON };
95#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
96#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
97
98int nfsrv_ticks;
99
100struct nfssvc_sockhead nfssvc_sockhead;
101int nfssvc_sockhead_flag;
102struct nfsd_head nfsd_head;
103int nfsd_head_flag;
104
105static int nfs_prev_nfssvc_sy_narg;
106static sy_call_t *nfs_prev_nfssvc_sy_call;
107
108/*
109 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
110 */
111int nfsrv_nfsv3_procid[NFS_NPROCS] = {
112	NFSPROC_NULL,
113	NFSPROC_GETATTR,
114	NFSPROC_SETATTR,
115	NFSPROC_NOOP,
116	NFSPROC_LOOKUP,
117	NFSPROC_READLINK,
118	NFSPROC_READ,
119	NFSPROC_NOOP,
120	NFSPROC_WRITE,
121	NFSPROC_CREATE,
122	NFSPROC_REMOVE,
123	NFSPROC_RENAME,
124	NFSPROC_LINK,
125	NFSPROC_SYMLINK,
126	NFSPROC_MKDIR,
127	NFSPROC_RMDIR,
128	NFSPROC_READDIR,
129	NFSPROC_FSSTAT,
130	NFSPROC_NOOP,
131	NFSPROC_NOOP,
132	NFSPROC_NOOP,
133	NFSPROC_NOOP,
134	NFSPROC_NOOP,
135};
136
137/*
138 * and the reverse mapping from generic to Version 2 procedure numbers
139 */
140int nfsrvv2_procid[NFS_NPROCS] = {
141	NFSV2PROC_NULL,
142	NFSV2PROC_GETATTR,
143	NFSV2PROC_SETATTR,
144	NFSV2PROC_LOOKUP,
145	NFSV2PROC_NOOP,
146	NFSV2PROC_READLINK,
147	NFSV2PROC_READ,
148	NFSV2PROC_WRITE,
149	NFSV2PROC_CREATE,
150	NFSV2PROC_MKDIR,
151	NFSV2PROC_SYMLINK,
152	NFSV2PROC_CREATE,
153	NFSV2PROC_REMOVE,
154	NFSV2PROC_RMDIR,
155	NFSV2PROC_RENAME,
156	NFSV2PROC_LINK,
157	NFSV2PROC_READDIR,
158	NFSV2PROC_NOOP,
159	NFSV2PROC_STATFS,
160	NFSV2PROC_NOOP,
161	NFSV2PROC_NOOP,
162	NFSV2PROC_NOOP,
163	NFSV2PROC_NOOP,
164};
165
166/*
167 * Maps errno values to nfs error numbers.
168 * Use 0 (which gets converted to NFSERR_IO) as the catch all for ones not
169 * specifically defined in RFC 1094.
170 */
171static u_char nfsrv_v2errmap[ELAST] = {
172  NFSERR_PERM,	NFSERR_NOENT,	0,		0,		0,
173  NFSERR_NXIO,	0,		0,		0,		0,
174  0,		0,		NFSERR_ACCES,	0,		0,
175  0,		NFSERR_EXIST,	0,		NFSERR_NODEV,	NFSERR_NOTDIR,
176  NFSERR_ISDIR,	0,		0,		0,		0,
177  0,		NFSERR_FBIG,	NFSERR_NOSPC,	0,		NFSERR_ROFS,
178  0,		0,		0,		0,		0,
179  0,		0,		0,		0,		0,
180  0,		0,		0,		0,		0,
181  0,		0,		0,		0,		0,
182  0,		0,		0,		0,		0,
183  0,		0,		0,		0,		0,
184  0,		0,		NFSERR_NAMETOL,	0,		0,
185  NFSERR_NOTEMPTY, 0,		0,		NFSERR_DQUOT,	NFSERR_STALE,
186  0
187};
188
189/*
190 * Maps errno values to nfs error numbers.
191 * Although it is not obvious whether or not NFS clients really care if
192 * a returned error value is in the specified list for the procedure, the
193 * safest thing to do is filter them appropriately. For Version 2, the
194 * X/Open XNFS document is the only specification that defines error values
195 * for each RPC (The RFC simply lists all possible error values for all RPCs),
196 * so I have decided to not do this for Version 2.
197 * The first entry is the default error return and the rest are the valid
198 * errors for that RPC in increasing numeric order.
199 */
200static short nfsv3err_null[] = {
201	0,
202	0,
203};
204
205static short nfsv3err_getattr[] = {
206	NFSERR_IO,
207	NFSERR_IO,
208	NFSERR_STALE,
209	NFSERR_BADHANDLE,
210	NFSERR_SERVERFAULT,
211	0,
212};
213
214static short nfsv3err_setattr[] = {
215	NFSERR_IO,
216	NFSERR_PERM,
217	NFSERR_IO,
218	NFSERR_ACCES,
219	NFSERR_INVAL,
220	NFSERR_NOSPC,
221	NFSERR_ROFS,
222	NFSERR_DQUOT,
223	NFSERR_STALE,
224	NFSERR_BADHANDLE,
225	NFSERR_NOT_SYNC,
226	NFSERR_SERVERFAULT,
227	0,
228};
229
230static short nfsv3err_lookup[] = {
231	NFSERR_IO,
232	NFSERR_NOENT,
233	NFSERR_IO,
234	NFSERR_ACCES,
235	NFSERR_NOTDIR,
236	NFSERR_NAMETOL,
237	NFSERR_STALE,
238	NFSERR_BADHANDLE,
239	NFSERR_SERVERFAULT,
240	0,
241};
242
243static short nfsv3err_access[] = {
244	NFSERR_IO,
245	NFSERR_IO,
246	NFSERR_STALE,
247	NFSERR_BADHANDLE,
248	NFSERR_SERVERFAULT,
249	0,
250};
251
252static short nfsv3err_readlink[] = {
253	NFSERR_IO,
254	NFSERR_IO,
255	NFSERR_ACCES,
256	NFSERR_INVAL,
257	NFSERR_STALE,
258	NFSERR_BADHANDLE,
259	NFSERR_NOTSUPP,
260	NFSERR_SERVERFAULT,
261	0,
262};
263
264static short nfsv3err_read[] = {
265	NFSERR_IO,
266	NFSERR_IO,
267	NFSERR_NXIO,
268	NFSERR_ACCES,
269	NFSERR_INVAL,
270	NFSERR_STALE,
271	NFSERR_BADHANDLE,
272	NFSERR_SERVERFAULT,
273	0,
274};
275
276static short nfsv3err_write[] = {
277	NFSERR_IO,
278	NFSERR_IO,
279	NFSERR_ACCES,
280	NFSERR_INVAL,
281	NFSERR_FBIG,
282	NFSERR_NOSPC,
283	NFSERR_ROFS,
284	NFSERR_DQUOT,
285	NFSERR_STALE,
286	NFSERR_BADHANDLE,
287	NFSERR_SERVERFAULT,
288	0,
289};
290
291static short nfsv3err_create[] = {
292	NFSERR_IO,
293	NFSERR_IO,
294	NFSERR_ACCES,
295	NFSERR_EXIST,
296	NFSERR_NOTDIR,
297	NFSERR_NOSPC,
298	NFSERR_ROFS,
299	NFSERR_NAMETOL,
300	NFSERR_DQUOT,
301	NFSERR_STALE,
302	NFSERR_BADHANDLE,
303	NFSERR_NOTSUPP,
304	NFSERR_SERVERFAULT,
305	0,
306};
307
308static short nfsv3err_mkdir[] = {
309	NFSERR_IO,
310	NFSERR_IO,
311	NFSERR_ACCES,
312	NFSERR_EXIST,
313	NFSERR_NOTDIR,
314	NFSERR_NOSPC,
315	NFSERR_ROFS,
316	NFSERR_NAMETOL,
317	NFSERR_DQUOT,
318	NFSERR_STALE,
319	NFSERR_BADHANDLE,
320	NFSERR_NOTSUPP,
321	NFSERR_SERVERFAULT,
322	0,
323};
324
325static short nfsv3err_symlink[] = {
326	NFSERR_IO,
327	NFSERR_IO,
328	NFSERR_ACCES,
329	NFSERR_EXIST,
330	NFSERR_NOTDIR,
331	NFSERR_NOSPC,
332	NFSERR_ROFS,
333	NFSERR_NAMETOL,
334	NFSERR_DQUOT,
335	NFSERR_STALE,
336	NFSERR_BADHANDLE,
337	NFSERR_NOTSUPP,
338	NFSERR_SERVERFAULT,
339	0,
340};
341
342static short nfsv3err_mknod[] = {
343	NFSERR_IO,
344	NFSERR_IO,
345	NFSERR_ACCES,
346	NFSERR_EXIST,
347	NFSERR_NOTDIR,
348	NFSERR_NOSPC,
349	NFSERR_ROFS,
350	NFSERR_NAMETOL,
351	NFSERR_DQUOT,
352	NFSERR_STALE,
353	NFSERR_BADHANDLE,
354	NFSERR_NOTSUPP,
355	NFSERR_SERVERFAULT,
356	NFSERR_BADTYPE,
357	0,
358};
359
360static short nfsv3err_remove[] = {
361	NFSERR_IO,
362	NFSERR_NOENT,
363	NFSERR_IO,
364	NFSERR_ACCES,
365	NFSERR_NOTDIR,
366	NFSERR_ROFS,
367	NFSERR_NAMETOL,
368	NFSERR_STALE,
369	NFSERR_BADHANDLE,
370	NFSERR_SERVERFAULT,
371	0,
372};
373
374static short nfsv3err_rmdir[] = {
375	NFSERR_IO,
376	NFSERR_NOENT,
377	NFSERR_IO,
378	NFSERR_ACCES,
379	NFSERR_EXIST,
380	NFSERR_NOTDIR,
381	NFSERR_INVAL,
382	NFSERR_ROFS,
383	NFSERR_NAMETOL,
384	NFSERR_NOTEMPTY,
385	NFSERR_STALE,
386	NFSERR_BADHANDLE,
387	NFSERR_NOTSUPP,
388	NFSERR_SERVERFAULT,
389	0,
390};
391
392static short nfsv3err_rename[] = {
393	NFSERR_IO,
394	NFSERR_NOENT,
395	NFSERR_IO,
396	NFSERR_ACCES,
397	NFSERR_EXIST,
398	NFSERR_XDEV,
399	NFSERR_NOTDIR,
400	NFSERR_ISDIR,
401	NFSERR_INVAL,
402	NFSERR_NOSPC,
403	NFSERR_ROFS,
404	NFSERR_MLINK,
405	NFSERR_NAMETOL,
406	NFSERR_NOTEMPTY,
407	NFSERR_DQUOT,
408	NFSERR_STALE,
409	NFSERR_BADHANDLE,
410	NFSERR_NOTSUPP,
411	NFSERR_SERVERFAULT,
412	0,
413};
414
415static short nfsv3err_link[] = {
416	NFSERR_IO,
417	NFSERR_IO,
418	NFSERR_ACCES,
419	NFSERR_EXIST,
420	NFSERR_XDEV,
421	NFSERR_NOTDIR,
422	NFSERR_INVAL,
423	NFSERR_NOSPC,
424	NFSERR_ROFS,
425	NFSERR_MLINK,
426	NFSERR_NAMETOL,
427	NFSERR_DQUOT,
428	NFSERR_STALE,
429	NFSERR_BADHANDLE,
430	NFSERR_NOTSUPP,
431	NFSERR_SERVERFAULT,
432	0,
433};
434
435static short nfsv3err_readdir[] = {
436	NFSERR_IO,
437	NFSERR_IO,
438	NFSERR_ACCES,
439	NFSERR_NOTDIR,
440	NFSERR_STALE,
441	NFSERR_BADHANDLE,
442	NFSERR_BAD_COOKIE,
443	NFSERR_TOOSMALL,
444	NFSERR_SERVERFAULT,
445	0,
446};
447
448static short nfsv3err_readdirplus[] = {
449	NFSERR_IO,
450	NFSERR_IO,
451	NFSERR_ACCES,
452	NFSERR_NOTDIR,
453	NFSERR_STALE,
454	NFSERR_BADHANDLE,
455	NFSERR_BAD_COOKIE,
456	NFSERR_NOTSUPP,
457	NFSERR_TOOSMALL,
458	NFSERR_SERVERFAULT,
459	0,
460};
461
462static short nfsv3err_fsstat[] = {
463	NFSERR_IO,
464	NFSERR_IO,
465	NFSERR_STALE,
466	NFSERR_BADHANDLE,
467	NFSERR_SERVERFAULT,
468	0,
469};
470
471static short nfsv3err_fsinfo[] = {
472	NFSERR_STALE,
473	NFSERR_STALE,
474	NFSERR_BADHANDLE,
475	NFSERR_SERVERFAULT,
476	0,
477};
478
479static short nfsv3err_pathconf[] = {
480	NFSERR_STALE,
481	NFSERR_STALE,
482	NFSERR_BADHANDLE,
483	NFSERR_SERVERFAULT,
484	0,
485};
486
487static short nfsv3err_commit[] = {
488	NFSERR_IO,
489	NFSERR_IO,
490	NFSERR_STALE,
491	NFSERR_BADHANDLE,
492	NFSERR_SERVERFAULT,
493	0,
494};
495
496static short *nfsrv_v3errmap[] = {
497	nfsv3err_null,
498	nfsv3err_getattr,
499	nfsv3err_setattr,
500	nfsv3err_lookup,
501	nfsv3err_access,
502	nfsv3err_readlink,
503	nfsv3err_read,
504	nfsv3err_write,
505	nfsv3err_create,
506	nfsv3err_mkdir,
507	nfsv3err_symlink,
508	nfsv3err_mknod,
509	nfsv3err_remove,
510	nfsv3err_rmdir,
511	nfsv3err_rename,
512	nfsv3err_link,
513	nfsv3err_readdir,
514	nfsv3err_readdirplus,
515	nfsv3err_fsstat,
516	nfsv3err_fsinfo,
517	nfsv3err_pathconf,
518	nfsv3err_commit,
519};
520
521/*
522 * Called once to initialize data structures...
523 */
524static int
525nfsrv_modevent(module_t mod, int type, void *data)
526{
527
528	switch (type) {
529	case MOD_LOAD:
530		nfsrv_rpc_vers = txdr_unsigned(RPC_VER2);
531		nfsrv_rpc_call = txdr_unsigned(RPC_CALL);
532		nfsrv_rpc_reply = txdr_unsigned(RPC_REPLY);
533		nfsrv_rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
534		nfsrv_rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
535		nfsrv_rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
536		nfsrv_rpc_autherr = txdr_unsigned(RPC_AUTHERR);
537		nfsrv_rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
538		nfsrv_nfs_prog = txdr_unsigned(NFS_PROG);
539		nfsrv_nfs_true = txdr_unsigned(TRUE);
540		nfsrv_nfs_false = txdr_unsigned(FALSE);
541		nfsrv_nfs_xdrneg1 = txdr_unsigned(-1);
542		nfsrv_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
543		if (nfsrv_ticks < 1)
544			nfsrv_ticks = 1;
545
546		nfsrv_init(0);		/* Init server data structures */
547		nfsrv_initcache();	/* Init the server request cache */
548
549		nfsrv_timer(0);
550
551		nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
552		sysent[SYS_nfssvc].sy_narg = 2;
553		nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
554		sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
555		break;
556
557		case MOD_UNLOAD:
558
559		untimeout(nfsrv_timer, (void *)NULL, nfsrv_timer_handle);
560		sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
561		sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
562		break;
563	}
564	return 0;
565}
566static moduledata_t nfsserver_mod = {
567	"nfsserver",
568	nfsrv_modevent,
569	NULL,
570};
571DECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
572
573/* So that loader and kldload(2) can find us, wherever we are.. */
574MODULE_VERSION(nfsserver, 1);
575
576/*
577 * Set up nameidata for a lookup() call and do it.
578 *
579 * If pubflag is set, this call is done for a lookup operation on the
580 * public filehandle. In that case we allow crossing mountpoints and
581 * absolute pathnames. However, the caller is expected to check that
582 * the lookup result is within the public fs, and deny access if
583 * it is not.
584 *
585 * nfs_namei() clears out garbage fields that namei() might leave garbage.
586 * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
587 * error occurs but the parent was not requested.
588 *
589 * dirp may be set whether an error is returned or not, and must be
590 * released by the caller.
591 */
592int
593nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
594    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
595    caddr_t *dposp, struct vnode **retdirp, struct thread *td, int pubflag)
596{
597	int i, rem;
598	struct mbuf *md;
599	char *fromcp, *tocp, *cp;
600	struct iovec aiov;
601	struct uio auio;
602	struct vnode *dp;
603	int error, rdonly, linklen;
604	struct componentname *cnp = &ndp->ni_cnd;
605
606	*retdirp = NULL;
607	cnp->cn_flags |= NOMACCHECK;
608	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
609
610	/*
611	 * Copy the name from the mbuf list to ndp->ni_pnbuf
612	 * and set the various ndp fields appropriately.
613	 */
614	fromcp = *dposp;
615	tocp = cnp->cn_pnbuf;
616	md = *mdp;
617	rem = mtod(md, caddr_t) + md->m_len - fromcp;
618	for (i = 0; i < len; i++) {
619		while (rem == 0) {
620			md = md->m_next;
621			if (md == NULL) {
622				error = EBADRPC;
623				goto out;
624			}
625			fromcp = mtod(md, caddr_t);
626			rem = md->m_len;
627		}
628		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
629			error = EACCES;
630			goto out;
631		}
632		*tocp++ = *fromcp++;
633		rem--;
634	}
635	*tocp = '\0';
636	*mdp = md;
637	*dposp = fromcp;
638	len = nfsm_rndup(len)-len;
639	if (len > 0) {
640		if (rem >= len)
641			*dposp += len;
642		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
643			goto out;
644	}
645
646	/*
647	 * Extract and set starting directory.
648	 */
649	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
650	    nam, &rdonly, pubflag);
651	if (error)
652		goto out;
653	if (dp->v_type != VDIR) {
654		vrele(dp);
655		error = ENOTDIR;
656		goto out;
657	}
658
659	if (rdonly)
660		cnp->cn_flags |= RDONLY;
661
662	/*
663	 * Set return directory.  Reference to dp is implicitly transfered
664	 * to the returned pointer
665	 */
666	*retdirp = dp;
667
668	if (pubflag) {
669		/*
670		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
671		 * and the 'native path' indicator.
672		 */
673		cp = uma_zalloc(namei_zone, M_WAITOK);
674		fromcp = cnp->cn_pnbuf;
675		tocp = cp;
676		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
677			switch ((unsigned char)*fromcp) {
678			case WEBNFS_NATIVE_CHAR:
679				/*
680				 * 'Native' path for us is the same
681				 * as a path according to the NFS spec,
682				 * just skip the escape char.
683				 */
684				fromcp++;
685				break;
686			/*
687			 * More may be added in the future, range 0x80-0xff
688			 */
689			default:
690				error = EIO;
691				uma_zfree(namei_zone, cp);
692				goto out;
693			}
694		}
695		/*
696		 * Translate the '%' escapes, URL-style.
697		 */
698		while (*fromcp != '\0') {
699			if (*fromcp == WEBNFS_ESC_CHAR) {
700				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
701					fromcp++;
702					*tocp++ = HEXSTRTOI(fromcp);
703					fromcp += 2;
704					continue;
705				} else {
706					error = ENOENT;
707					uma_zfree(namei_zone, cp);
708					goto out;
709				}
710			} else
711				*tocp++ = *fromcp++;
712		}
713		*tocp = '\0';
714		uma_zfree(namei_zone, cnp->cn_pnbuf);
715		cnp->cn_pnbuf = cp;
716	}
717
718	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
719	ndp->ni_segflg = UIO_SYSSPACE;
720
721	if (pubflag) {
722		ndp->ni_rootdir = rootvnode;
723		ndp->ni_loopcnt = 0;
724		if (cnp->cn_pnbuf[0] == '/')
725			dp = rootvnode;
726	} else {
727		cnp->cn_flags |= NOCROSSMOUNT;
728	}
729
730	/*
731	 * Initialize for scan, set ni_startdir and bump ref on dp again
732	 * becuase lookup() will dereference ni_startdir.
733	 */
734
735	cnp->cn_thread = td;
736	VREF(dp);
737	ndp->ni_startdir = dp;
738
739	for (;;) {
740		cnp->cn_nameptr = cnp->cn_pnbuf;
741		/*
742		 * Call lookup() to do the real work.  If an error occurs,
743		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
744		 * we do not have to dereference anything before returning.
745		 * In either case ni_startdir will be dereferenced and NULLed
746		 * out.
747		 */
748		error = lookup(ndp);
749		if (error)
750			break;
751
752		/*
753		 * Check for encountering a symbolic link.  Trivial
754		 * termination occurs if no symlink encountered.
755		 * Note: zfree is safe because error is 0, so we will
756		 * not zfree it again when we break.
757		 */
758		if ((cnp->cn_flags & ISSYMLINK) == 0) {
759			nfsrv_object_create(ndp->ni_vp);
760			if (cnp->cn_flags & (SAVENAME | SAVESTART))
761				cnp->cn_flags |= HASBUF;
762			else
763				uma_zfree(namei_zone, cnp->cn_pnbuf);
764			break;
765		}
766
767		/*
768		 * Validate symlink
769		 */
770		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
771			VOP_UNLOCK(ndp->ni_dvp, 0, td);
772		if (!pubflag) {
773			error = EINVAL;
774			goto badlink2;
775		}
776
777		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
778			error = ELOOP;
779			goto badlink2;
780		}
781		if (ndp->ni_pathlen > 1)
782			cp = uma_zalloc(namei_zone, M_WAITOK);
783		else
784			cp = cnp->cn_pnbuf;
785		aiov.iov_base = cp;
786		aiov.iov_len = MAXPATHLEN;
787		auio.uio_iov = &aiov;
788		auio.uio_iovcnt = 1;
789		auio.uio_offset = 0;
790		auio.uio_rw = UIO_READ;
791		auio.uio_segflg = UIO_SYSSPACE;
792		auio.uio_td = NULL;
793		auio.uio_resid = MAXPATHLEN;
794		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
795		if (error) {
796		badlink1:
797			if (ndp->ni_pathlen > 1)
798				uma_zfree(namei_zone, cp);
799		badlink2:
800			vrele(ndp->ni_dvp);
801			vput(ndp->ni_vp);
802			break;
803		}
804		linklen = MAXPATHLEN - auio.uio_resid;
805		if (linklen == 0) {
806			error = ENOENT;
807			goto badlink1;
808		}
809		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
810			error = ENAMETOOLONG;
811			goto badlink1;
812		}
813
814		/*
815		 * Adjust or replace path
816		 */
817		if (ndp->ni_pathlen > 1) {
818			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
819			uma_zfree(namei_zone, cnp->cn_pnbuf);
820			cnp->cn_pnbuf = cp;
821		} else
822			cnp->cn_pnbuf[linklen] = '\0';
823		ndp->ni_pathlen += linklen;
824
825		/*
826		 * Cleanup refs for next loop and check if root directory
827		 * should replace current directory.  Normally ni_dvp
828		 * becomes the new base directory and is cleaned up when
829		 * we loop.  Explicitly null pointers after invalidation
830		 * to clarify operation.
831		 */
832		vput(ndp->ni_vp);
833		ndp->ni_vp = NULL;
834
835		if (cnp->cn_pnbuf[0] == '/') {
836			vrele(ndp->ni_dvp);
837			ndp->ni_dvp = ndp->ni_rootdir;
838			VREF(ndp->ni_dvp);
839		}
840		ndp->ni_startdir = ndp->ni_dvp;
841		ndp->ni_dvp = NULL;
842	}
843
844	/*
845	 * nfs_namei() guarentees that fields will not contain garbage
846	 * whether an error occurs or not.  This allows the caller to track
847	 * cleanup state trivially.
848	 */
849out:
850	if (error) {
851		uma_zfree(namei_zone, cnp->cn_pnbuf);
852		ndp->ni_vp = NULL;
853		ndp->ni_dvp = NULL;
854		ndp->ni_startdir = NULL;
855		cnp->cn_flags &= ~HASBUF;
856	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
857		ndp->ni_dvp = NULL;
858	}
859	return (error);
860}
861
862/*
863 * A fiddled version of m_adj() that ensures null fill to a long
864 * boundary and only trims off the back end
865 */
866void
867nfsm_adj(struct mbuf *mp, int len, int nul)
868{
869	struct mbuf *m;
870	int count, i;
871	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 == NULL)
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	for (m = m->m_next;m;m = m->m_next)
918		m->m_len = 0;
919}
920
921/*
922 * Make these functions instead of macros, so that the kernel text size
923 * doesn't get too big...
924 */
925void
926nfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
927    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
928    struct mbuf **mbp, char **bposp)
929{
930	struct mbuf *mb = *mbp;
931	char *bpos = *bposp;
932	u_int32_t *tl;
933
934	if (before_ret) {
935		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
936		*tl = nfsrv_nfs_false;
937	} else {
938		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
939		*tl++ = nfsrv_nfs_true;
940		txdr_hyper(before_vap->va_size, tl);
941		tl += 2;
942		txdr_nfsv3time(&(before_vap->va_mtime), tl);
943		tl += 2;
944		txdr_nfsv3time(&(before_vap->va_ctime), tl);
945	}
946	*bposp = bpos;
947	*mbp = mb;
948	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
949}
950
951void
952nfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
953    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
954{
955	struct mbuf *mb = *mbp;
956	char *bpos = *bposp;
957	u_int32_t *tl;
958	struct nfs_fattr *fp;
959
960	if (after_ret) {
961		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
962		*tl = nfsrv_nfs_false;
963	} else {
964		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
965		*tl++ = nfsrv_nfs_true;
966		fp = (struct nfs_fattr *)tl;
967		nfsm_srvfattr(nfsd, after_vap, fp);
968	}
969	*mbp = mb;
970	*bposp = bpos;
971}
972
973void
974nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
975    struct nfs_fattr *fp)
976{
977
978	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
979	fp->fa_uid = txdr_unsigned(vap->va_uid);
980	fp->fa_gid = txdr_unsigned(vap->va_gid);
981	if (nfsd->nd_flag & ND_NFSV3) {
982		fp->fa_type = vtonfsv3_type(vap->va_type);
983		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
984		txdr_hyper(vap->va_size, &fp->fa3_size);
985		txdr_hyper(vap->va_bytes, &fp->fa3_used);
986		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
987		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
988		fp->fa3_fsid.nfsuquad[0] = 0;
989		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
990		fp->fa3_fileid.nfsuquad[0] = 0;
991		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
992		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
993		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
994		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
995	} else {
996		fp->fa_type = vtonfsv2_type(vap->va_type);
997		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
998		fp->fa2_size = txdr_unsigned(vap->va_size);
999		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
1000		if (vap->va_type == VFIFO)
1001			fp->fa2_rdev = 0xffffffff;
1002		else
1003			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
1004		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
1005		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
1006		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
1007		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
1008		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
1009		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
1010	}
1011}
1012
1013/*
1014 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
1015 * 	- look up fsid in mount list (if not found ret error)
1016 *	- get vp and export rights by calling VFS_FHTOVP()
1017 *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
1018 *	- if not lockflag unlock it with VOP_UNLOCK()
1019 */
1020int
1021nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
1022    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
1023    int *rdonlyp, int pubflag)
1024{
1025	struct thread *td = curthread; /* XXX */
1026	struct mount *mp;
1027	int i;
1028	struct ucred *credanon;
1029	int error, exflags;
1030#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
1031	struct sockaddr_int *saddr;
1032#endif
1033
1034	*vpp = NULL;
1035
1036	if (nfs_ispublicfh(fhp)) {
1037		if (!pubflag || !nfs_pub.np_valid)
1038			return (ESTALE);
1039		fhp = &nfs_pub.np_handle;
1040	}
1041
1042	mp = vfs_getvfs(&fhp->fh_fsid);
1043	if (!mp)
1044		return (ESTALE);
1045	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
1046	if (error)
1047		return (error);
1048	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
1049	if (error)
1050		return (error);
1051#ifdef MNT_EXNORESPORT
1052	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
1053		saddr = (struct sockaddr_in *)nam;
1054		if ((saddr->sin_family == AF_INET ||
1055		     saddr->sin_family == AF_INET6) &&
1056	/* same code for INET and INET6: sin*_port at same offet */
1057		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
1058			vput(*vpp);
1059			*vpp = NULL;
1060			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
1061		}
1062	}
1063#endif
1064	/*
1065	 * Check/setup credentials.
1066	 */
1067	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
1068		cred->cr_uid = credanon->cr_uid;
1069		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
1070			cred->cr_groups[i] = credanon->cr_groups[i];
1071		cred->cr_ngroups = i;
1072	}
1073	if (exflags & MNT_EXRDONLY)
1074		*rdonlyp = 1;
1075	else
1076		*rdonlyp = 0;
1077
1078	nfsrv_object_create(*vpp);
1079
1080	if (!lockflag)
1081		VOP_UNLOCK(*vpp, 0, td);
1082	return (0);
1083}
1084
1085
1086/*
1087 * WebNFS: check if a filehandle is a public filehandle. For v3, this
1088 * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
1089 * transformed this to all zeroes in both cases, so check for it.
1090 */
1091int
1092nfs_ispublicfh(fhandle_t *fhp)
1093{
1094	char *cp = (char *)fhp;
1095	int i;
1096
1097	for (i = 0; i < NFSX_V3FH; i++)
1098		if (*cp++ != 0)
1099			return (FALSE);
1100	return (TRUE);
1101}
1102
1103/*
1104 * This function compares two net addresses by family and returns TRUE
1105 * if they are the same host.
1106 * If there is any doubt, return FALSE.
1107 * The AF_INET family is handled as a special case so that address mbufs
1108 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
1109 */
1110int
1111netaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
1112{
1113	struct sockaddr_in *inetaddr;
1114
1115	switch (family) {
1116	case AF_INET:
1117		inetaddr = (struct sockaddr_in *)nam;
1118		if (inetaddr->sin_family == AF_INET &&
1119		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
1120			return (1);
1121		break;
1122#ifdef INET6
1123	case AF_INET6:
1124	{
1125		register struct sockaddr_in6 *inet6addr1, *inet6addr2;
1126
1127		inet6addr1 = (struct sockaddr_in6 *)nam;
1128		inet6addr2 = (struct sockaddr_in6 *)haddr->had_nam;
1129	/* XXX - should test sin6_scope_id ? */
1130		if (inet6addr1->sin6_family == AF_INET6 &&
1131		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
1132				       &inet6addr2->sin6_addr))
1133			return (1);
1134		break;
1135	}
1136#endif
1137	default:
1138		break;
1139	};
1140	return (0);
1141}
1142
1143/*
1144 * Map errnos to NFS error numbers. For Version 3 also filter out error
1145 * numbers not specified for the associated procedure.
1146 */
1147int
1148nfsrv_errmap(struct nfsrv_descript *nd, int err)
1149{
1150	short *defaulterrp, *errp;
1151	int e;
1152
1153	if (nd->nd_flag & ND_NFSV3) {
1154	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
1155		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
1156		while (*++errp) {
1157			if (*errp == err)
1158				return (err);
1159			else if (*errp > err)
1160				break;
1161		}
1162		return ((int)*defaulterrp);
1163	    } else
1164		return (err & 0xffff);
1165	}
1166	e = 0;
1167	if (err <= ELAST)
1168		e = nfsrv_v2errmap[err - 1];
1169	if (e != 0)
1170		return (e);
1171	return (NFSERR_IO);
1172}
1173
1174int
1175nfsrv_object_create(struct vnode *vp)
1176{
1177
1178	if (vp == NULL || vp->v_type != VREG)
1179		return (1);
1180	return (vfs_object_create(vp, curthread, curthread->td_ucred));
1181}
1182
1183/*
1184 * Sort the group list in increasing numerical order.
1185 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1186 *  that used to be here.)
1187 */
1188void
1189nfsrvw_sort(gid_t *list, int num)
1190{
1191	int i, j;
1192	gid_t v;
1193
1194	/* Insertion sort. */
1195	for (i = 1; i < num; i++) {
1196		v = list[i];
1197		/* find correct slot for value v, moving others up */
1198		for (j = i; --j >= 0 && v < list[j];)
1199			list[j + 1] = list[j];
1200		list[j + 1] = v;
1201	}
1202}
1203
1204/*
1205 * copy credentials making sure that the result can be compared with bcmp().
1206 */
1207void
1208nfsrv_setcred(struct ucred *incred, struct ucred *outcred)
1209{
1210	int i;
1211
1212	bzero((caddr_t)outcred, sizeof (struct ucred));
1213	outcred->cr_ref = 1;
1214	outcred->cr_uid = incred->cr_uid;
1215	outcred->cr_ngroups = incred->cr_ngroups;
1216	for (i = 0; i < incred->cr_ngroups; i++)
1217		outcred->cr_groups[i] = incred->cr_groups[i];
1218	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
1219}
1220
1221/*
1222 * Helper functions for macros.
1223 */
1224
1225void
1226nfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
1227{
1228	u_int32_t *tl;
1229
1230	if (v3) {
1231		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1232		*tl++ = txdr_unsigned(NFSX_V3FH);
1233		bcopy(f, tl, NFSX_V3FH);
1234	} else {
1235		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
1236		bcopy(f, tl, NFSX_V2FH);
1237	}
1238}
1239
1240void
1241nfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
1242{
1243	u_int32_t *tl;
1244
1245	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1246	*tl++ = nfsrv_nfs_true;
1247	*tl++ = txdr_unsigned(NFSX_V3FH);
1248	bcopy(f, tl, NFSX_V3FH);
1249}
1250
1251int
1252nfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1253{
1254	u_int32_t *tl;
1255
1256	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1257	if (tl == NULL)
1258		return EBADRPC;
1259	*s = fxdr_unsigned(int32_t, *tl);
1260	if (*s > m || *s <= 0)
1261		return EBADRPC;
1262	return 0;
1263}
1264
1265int
1266nfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1267{
1268	u_int32_t *tl;
1269
1270	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1271	if (tl == NULL)
1272		return EBADRPC;
1273	*s = fxdr_unsigned(int32_t, *tl);
1274	if (*s > m)
1275		return NFSERR_NAMETOL;
1276	if (*s <= 0)
1277		return EBADRPC;
1278	return 0;
1279}
1280
1281void
1282nfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1283    char **bp, char **be, caddr_t bpos)
1284{
1285	struct mbuf *nmp;
1286
1287	if (*bp >= *be) {
1288		if (*mp == mb)
1289			(*mp)->m_len += *bp - bpos;
1290		MGET(nmp, M_TRYWAIT, MT_DATA);
1291		MCLGET(nmp, M_TRYWAIT);
1292		nmp->m_len = NFSMSIZ(nmp);
1293		(*mp)->m_next = nmp;
1294		*mp = nmp;
1295		*bp = mtod(*mp, caddr_t);
1296		*be = *bp + (*mp)->m_len;
1297	}
1298	*tl = (u_int32_t *)*bp;
1299}
1300
1301int
1302nfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md,
1303    caddr_t *dpos)
1304{
1305	u_int32_t *tl;
1306	int fhlen;
1307
1308	if (nfsd->nd_flag & ND_NFSV3) {
1309		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1310		if (tl == NULL)
1311			return EBADRPC;
1312		fhlen = fxdr_unsigned(int, *tl);
1313		if (fhlen != 0 && fhlen != NFSX_V3FH)
1314			return EBADRPC;
1315	} else {
1316		fhlen = NFSX_V2FH;
1317	}
1318	if (fhlen != 0) {
1319		tl = nfsm_dissect_xx(fhlen, md, dpos);
1320		if (tl == NULL)
1321			return EBADRPC;
1322		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
1323	} else {
1324		bzero((caddr_t)(f), NFSX_V3FH);
1325	}
1326	return 0;
1327}
1328
1329int
1330nfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
1331{
1332	u_int32_t *tl;
1333
1334	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1335	if (tl == NULL)
1336		return EBADRPC;
1337	if (*tl == nfsrv_nfs_true) {
1338		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1339		if (tl == NULL)
1340			return EBADRPC;
1341		(a)->va_mode = nfstov_mode(*tl);
1342	}
1343	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1344	if (tl == NULL)
1345		return EBADRPC;
1346	if (*tl == nfsrv_nfs_true) {
1347		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1348		if (tl == NULL)
1349			return EBADRPC;
1350		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
1351	}
1352	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1353	if (tl == NULL)
1354		return EBADRPC;
1355	if (*tl == nfsrv_nfs_true) {
1356		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1357		if (tl == NULL)
1358			return EBADRPC;
1359		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
1360	}
1361	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1362	if (tl == NULL)
1363		return EBADRPC;
1364	if (*tl == nfsrv_nfs_true) {
1365		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
1366		if (tl == NULL)
1367			return EBADRPC;
1368		(a)->va_size = fxdr_hyper(tl);
1369	}
1370	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1371	if (tl == NULL)
1372		return EBADRPC;
1373	switch (fxdr_unsigned(int, *tl)) {
1374	case NFSV3SATTRTIME_TOCLIENT:
1375		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
1376		if (tl == NULL)
1377			return EBADRPC;
1378		fxdr_nfsv3time(tl, &(a)->va_atime);
1379		break;
1380	case NFSV3SATTRTIME_TOSERVER:
1381		getnanotime(&(a)->va_atime);
1382		break;
1383	}
1384	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1385	if (tl == NULL)
1386		return EBADRPC;
1387	switch (fxdr_unsigned(int, *tl)) {
1388	case NFSV3SATTRTIME_TOCLIENT:
1389		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
1390		if (tl == NULL)
1391			return EBADRPC;
1392		fxdr_nfsv3time(tl, &(a)->va_mtime);
1393		break;
1394	case NFSV3SATTRTIME_TOSERVER:
1395		getnanotime(&(a)->va_mtime);
1396		break;
1397	}
1398	return 0;
1399}
1400