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