nfs_srvsubs.c revision 225736
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: stable/9/sys/nfsserver/nfs_srvsubs.c 222167 2011-05-22 01:07:54Z rmacklem $");
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	int dvfslocked;
598	int vfslocked;
599
600	vfslocked = 0;
601	dvfslocked = 0;
602	*retdirp = NULL;
603	cnp->cn_flags |= NOMACCHECK;
604	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
605
606	/*
607	 * Copy the name from the mbuf list to ndp->ni_pnbuf
608	 * and set the various ndp fields appropriately.
609	 */
610	fromcp = *dposp;
611	tocp = cnp->cn_pnbuf;
612	md = *mdp;
613	rem = mtod(md, caddr_t) + md->m_len - fromcp;
614	for (i = 0; i < len; i++) {
615		while (rem == 0) {
616			md = md->m_next;
617			if (md == NULL) {
618				error = EBADRPC;
619				goto out;
620			}
621			fromcp = mtod(md, caddr_t);
622			rem = md->m_len;
623		}
624		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
625			error = EACCES;
626			goto out;
627		}
628		*tocp++ = *fromcp++;
629		rem--;
630	}
631	*tocp = '\0';
632	*mdp = md;
633	*dposp = fromcp;
634	len = nfsm_rndup(len)-len;
635	if (len > 0) {
636		if (rem >= len)
637			*dposp += len;
638		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
639			goto out;
640	}
641
642	if (!pubflag && nfs_ispublicfh(fhp))
643		return (ESTALE);
644
645	/*
646	 * Extract and set starting directory.
647	 */
648	error = nfsrv_fhtovp(fhp, 0, &dp, &dvfslocked, nfsd, slp, nam, &rdonly);
649	if (error)
650		goto out;
651	vfslocked = VFS_LOCK_GIANT(dp->v_mount);
652	if (dp->v_type != VDIR) {
653		vput(dp);
654		error = ENOTDIR;
655		goto out;
656	}
657
658	if (rdonly)
659		cnp->cn_flags |= RDONLY;
660
661	/*
662	 * Set return directory.  Reference to dp is implicitly transfered
663	 * to the returned pointer
664	 */
665	*retdirp = dp;
666	if (v3) {
667		*retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
668			ndp->ni_cnd.cn_cred);
669	}
670
671	VOP_UNLOCK(dp, 0);
672
673	if (pubflag) {
674		/*
675		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
676		 * and the 'native path' indicator.
677		 */
678		cp = uma_zalloc(namei_zone, M_WAITOK);
679		fromcp = cnp->cn_pnbuf;
680		tocp = cp;
681		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
682			switch ((unsigned char)*fromcp) {
683			case WEBNFS_NATIVE_CHAR:
684				/*
685				 * 'Native' path for us is the same
686				 * as a path according to the NFS spec,
687				 * just skip the escape char.
688				 */
689				fromcp++;
690				break;
691			/*
692			 * More may be added in the future, range 0x80-0xff
693			 */
694			default:
695				error = EIO;
696				uma_zfree(namei_zone, cp);
697				goto out;
698			}
699		}
700		/*
701		 * Translate the '%' escapes, URL-style.
702		 */
703		while (*fromcp != '\0') {
704			if (*fromcp == WEBNFS_ESC_CHAR) {
705				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
706					fromcp++;
707					*tocp++ = HEXSTRTOI(fromcp);
708					fromcp += 2;
709					continue;
710				} else {
711					error = ENOENT;
712					uma_zfree(namei_zone, cp);
713					goto out;
714				}
715			} else
716				*tocp++ = *fromcp++;
717		}
718		*tocp = '\0';
719		uma_zfree(namei_zone, cnp->cn_pnbuf);
720		cnp->cn_pnbuf = cp;
721	}
722
723	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
724	ndp->ni_segflg = UIO_SYSSPACE;
725
726	if (pubflag) {
727		ndp->ni_rootdir = rootvnode;
728		ndp->ni_loopcnt = 0;
729		if (cnp->cn_pnbuf[0] == '/') {
730			int tvfslocked;
731
732			tvfslocked = VFS_LOCK_GIANT(rootvnode->v_mount);
733			VFS_UNLOCK_GIANT(vfslocked);
734			dp = rootvnode;
735			vfslocked = tvfslocked;
736		}
737	} else {
738		cnp->cn_flags |= NOCROSSMOUNT;
739	}
740
741	/*
742	 * Initialize for scan, set ni_startdir and bump ref on dp again
743	 * because lookup() will dereference ni_startdir.
744	 */
745
746	cnp->cn_thread = curthread;
747	VREF(dp);
748	ndp->ni_startdir = dp;
749
750	if (!lockleaf)
751		cnp->cn_flags |= LOCKLEAF;
752	for (;;) {
753		cnp->cn_nameptr = cnp->cn_pnbuf;
754		/*
755		 * Call lookup() to do the real work.  If an error occurs,
756		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
757		 * we do not have to dereference anything before returning.
758		 * In either case ni_startdir will be dereferenced and NULLed
759		 * out.
760		 */
761		if (vfslocked)
762			ndp->ni_cnd.cn_flags |= GIANTHELD;
763		error = lookup(ndp);
764		vfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0;
765		ndp->ni_cnd.cn_flags &= ~GIANTHELD;
766		if (error)
767			break;
768
769		/*
770		 * Check for encountering a symbolic link.  Trivial
771		 * termination occurs if no symlink encountered.
772		 * Note: zfree is safe because error is 0, so we will
773		 * not zfree it again when we break.
774		 */
775		if ((cnp->cn_flags & ISSYMLINK) == 0) {
776			if (cnp->cn_flags & (SAVENAME | SAVESTART))
777				cnp->cn_flags |= HASBUF;
778			else
779				uma_zfree(namei_zone, cnp->cn_pnbuf);
780			if (ndp->ni_vp && !lockleaf)
781				VOP_UNLOCK(ndp->ni_vp, 0);
782			break;
783		}
784
785		/*
786		 * Validate symlink
787		 */
788		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
789			VOP_UNLOCK(ndp->ni_dvp, 0);
790		if (!pubflag) {
791			error = EINVAL;
792			goto badlink2;
793		}
794
795		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
796			error = ELOOP;
797			goto badlink2;
798		}
799		if (ndp->ni_pathlen > 1)
800			cp = uma_zalloc(namei_zone, M_WAITOK);
801		else
802			cp = cnp->cn_pnbuf;
803		aiov.iov_base = cp;
804		aiov.iov_len = MAXPATHLEN;
805		auio.uio_iov = &aiov;
806		auio.uio_iovcnt = 1;
807		auio.uio_offset = 0;
808		auio.uio_rw = UIO_READ;
809		auio.uio_segflg = UIO_SYSSPACE;
810		auio.uio_td = NULL;
811		auio.uio_resid = MAXPATHLEN;
812		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
813		if (error) {
814		badlink1:
815			if (ndp->ni_pathlen > 1)
816				uma_zfree(namei_zone, cp);
817		badlink2:
818			vput(ndp->ni_vp);
819			vrele(ndp->ni_dvp);
820			break;
821		}
822		linklen = MAXPATHLEN - auio.uio_resid;
823		if (linklen == 0) {
824			error = ENOENT;
825			goto badlink1;
826		}
827		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
828			error = ENAMETOOLONG;
829			goto badlink1;
830		}
831
832		/*
833		 * Adjust or replace path
834		 */
835		if (ndp->ni_pathlen > 1) {
836			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
837			uma_zfree(namei_zone, cnp->cn_pnbuf);
838			cnp->cn_pnbuf = cp;
839		} else
840			cnp->cn_pnbuf[linklen] = '\0';
841		ndp->ni_pathlen += linklen;
842
843		/*
844		 * Cleanup refs for next loop and check if root directory
845		 * should replace current directory.  Normally ni_dvp
846		 * becomes the new base directory and is cleaned up when
847		 * we loop.  Explicitly null pointers after invalidation
848		 * to clarify operation.
849		 */
850		vput(ndp->ni_vp);
851		ndp->ni_vp = NULL;
852
853		if (cnp->cn_pnbuf[0] == '/') {
854			vrele(ndp->ni_dvp);
855			ndp->ni_dvp = ndp->ni_rootdir;
856			VREF(ndp->ni_dvp);
857		}
858		ndp->ni_startdir = ndp->ni_dvp;
859		ndp->ni_dvp = NULL;
860	}
861	if (!lockleaf)
862		cnp->cn_flags &= ~LOCKLEAF;
863	if (cnp->cn_flags & GIANTHELD) {
864		mtx_unlock(&Giant);
865		cnp->cn_flags &= ~GIANTHELD;
866	}
867
868	/*
869	 * nfs_namei() guarentees that fields will not contain garbage
870	 * whether an error occurs or not.  This allows the caller to track
871	 * cleanup state trivially.
872	 */
873out:
874	if (error) {
875		uma_zfree(namei_zone, cnp->cn_pnbuf);
876		ndp->ni_vp = NULL;
877		ndp->ni_dvp = NULL;
878		ndp->ni_startdir = NULL;
879		cnp->cn_flags &= ~HASBUF;
880		VFS_UNLOCK_GIANT(vfslocked);
881		vfslocked = 0;
882	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
883		ndp->ni_dvp = NULL;
884	}
885	/*
886	 * This differs from normal namei() in that even on failure we may
887	 * return with Giant held due to the dirp return.  Make sure we only
888	 * have not recursed however.  The calling code only expects to drop
889	 * one acquire.
890	 */
891	if (vfslocked || dvfslocked)
892		ndp->ni_cnd.cn_flags |= GIANTHELD;
893	if (vfslocked && dvfslocked)
894		VFS_UNLOCK_GIANT(vfslocked);
895	return (error);
896}
897
898/*
899 * A fiddled version of m_adj() that ensures null fill to a long
900 * boundary and only trims off the back end
901 */
902void
903nfsm_adj(struct mbuf *mp, int len, int nul)
904{
905	struct mbuf *m;
906	int count, i;
907	char *cp;
908
909	/*
910	 * Trim from tail.  Scan the mbuf chain,
911	 * calculating its length and finding the last mbuf.
912	 * If the adjustment only affects this mbuf, then just
913	 * adjust and return.  Otherwise, rescan and truncate
914	 * after the remaining size.
915	 */
916	count = 0;
917	m = mp;
918	for (;;) {
919		count += m->m_len;
920		if (m->m_next == NULL)
921			break;
922		m = m->m_next;
923	}
924	if (m->m_len > len) {
925		m->m_len -= len;
926		if (nul > 0) {
927			cp = mtod(m, caddr_t)+m->m_len-nul;
928			for (i = 0; i < nul; i++)
929				*cp++ = '\0';
930		}
931		return;
932	}
933	count -= len;
934	if (count < 0)
935		count = 0;
936	/*
937	 * Correct length for chain is "count".
938	 * Find the mbuf with last data, adjust its length,
939	 * and toss data from remaining mbufs on chain.
940	 */
941	for (m = mp; m; m = m->m_next) {
942		if (m->m_len >= count) {
943			m->m_len = count;
944			if (nul > 0) {
945				cp = mtod(m, caddr_t)+m->m_len-nul;
946				for (i = 0; i < nul; i++)
947					*cp++ = '\0';
948			}
949			if (m->m_next != NULL) {
950				m_freem(m->m_next);
951				m->m_next = NULL;
952			}
953			break;
954		}
955		count -= m->m_len;
956	}
957}
958
959/*
960 * Make these functions instead of macros, so that the kernel text size
961 * doesn't get too big...
962 */
963void
964nfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
965    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
966    struct mbuf **mbp, char **bposp)
967{
968	struct mbuf *mb = *mbp;
969	char *bpos = *bposp;
970	u_int32_t *tl;
971
972	if (before_ret) {
973		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
974		*tl = nfsrv_nfs_false;
975	} else {
976		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
977		*tl++ = nfsrv_nfs_true;
978		txdr_hyper(before_vap->va_size, tl);
979		tl += 2;
980		txdr_nfsv3time(&(before_vap->va_mtime), tl);
981		tl += 2;
982		txdr_nfsv3time(&(before_vap->va_ctime), tl);
983	}
984	*bposp = bpos;
985	*mbp = mb;
986	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
987}
988
989void
990nfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
991    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
992{
993	struct mbuf *mb = *mbp;
994	char *bpos = *bposp;
995	u_int32_t *tl;
996	struct nfs_fattr *fp;
997
998	if (after_ret) {
999		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
1000		*tl = nfsrv_nfs_false;
1001	} else {
1002		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
1003		*tl++ = nfsrv_nfs_true;
1004		fp = (struct nfs_fattr *)tl;
1005		nfsm_srvfattr(nfsd, after_vap, fp);
1006	}
1007	*mbp = mb;
1008	*bposp = bpos;
1009}
1010
1011void
1012nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
1013    struct nfs_fattr *fp)
1014{
1015
1016	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
1017	fp->fa_uid = txdr_unsigned(vap->va_uid);
1018	fp->fa_gid = txdr_unsigned(vap->va_gid);
1019	if (nfsd->nd_flag & ND_NFSV3) {
1020		fp->fa_type = vtonfsv3_type(vap->va_type);
1021		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
1022		txdr_hyper(vap->va_size, &fp->fa3_size);
1023		txdr_hyper(vap->va_bytes, &fp->fa3_used);
1024		fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev));
1025		fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
1026		fp->fa3_fsid.nfsuquad[0] = 0;
1027		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
1028		fp->fa3_fileid.nfsuquad[0] = 0;
1029		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
1030		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
1031		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
1032		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
1033	} else {
1034		fp->fa_type = vtonfsv2_type(vap->va_type);
1035		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
1036		fp->fa2_size = txdr_unsigned(vap->va_size);
1037		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
1038		if (vap->va_type == VFIFO)
1039			fp->fa2_rdev = 0xffffffff;
1040		else
1041			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
1042		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
1043		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
1044		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
1045		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
1046		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
1047		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
1048	}
1049}
1050
1051/*
1052 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
1053 * 	- look up fsid in mount list (if not found ret error)
1054 *	- get vp and export rights by calling VFS_FHTOVP()
1055 *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
1056 */
1057int
1058nfsrv_fhtovp(fhandle_t *fhp, int flags, struct vnode **vpp, int *vfslockedp,
1059    struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
1060    struct sockaddr *nam, int *rdonlyp)
1061{
1062	struct mount *mp;
1063	int i;
1064	struct ucred *cred, *credanon;
1065	int error, exflags;
1066#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
1067	struct sockaddr_int *saddr;
1068#endif
1069	int credflavor;
1070	int vfslocked;
1071	int numsecflavors, *secflavors;
1072	int authsys;
1073	int v3 = nfsd->nd_flag & ND_NFSV3;
1074	int mountreq;
1075
1076	*vfslockedp = 0;
1077	*vpp = NULL;
1078
1079	if (nfs_ispublicfh(fhp)) {
1080		if (!nfs_pub.np_valid)
1081			return (ESTALE);
1082		fhp = &nfs_pub.np_handle;
1083	}
1084
1085	mp = vfs_busyfs(&fhp->fh_fsid);
1086	if (!mp)
1087		return (ESTALE);
1088	vfslocked = VFS_LOCK_GIANT(mp);
1089	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon,
1090	    &numsecflavors, &secflavors);
1091	if (error) {
1092		vfs_unbusy(mp);
1093		goto out;
1094	}
1095	if (numsecflavors == 0) {
1096		/*
1097		 * This can happen if the system is running with an
1098		 * old mountd that doesn't pass in a secflavor list.
1099		 */
1100		numsecflavors = 1;
1101		authsys = AUTH_SYS;
1102		secflavors = &authsys;
1103	}
1104	credflavor = nfsd->nd_credflavor;
1105	for (i = 0; i < numsecflavors; i++) {
1106		if (secflavors[i] == credflavor)
1107			break;
1108	}
1109	if (i == numsecflavors) {
1110		/*
1111		 * RFC 2623 section 2.3.2 - allow certain procedures
1112		 * used at NFS client mount time even if they have
1113		 * weak authentication.
1114		 */
1115		mountreq = FALSE;
1116		if (v3) {
1117			if (nfsd->nd_procnum == NFSPROC_FSINFO
1118			    || nfsd->nd_procnum == NFSPROC_GETATTR)
1119				mountreq = TRUE;
1120		} else {
1121			if (nfsd->nd_procnum == NFSPROC_FSSTAT
1122			    || nfsd->nd_procnum == NFSPROC_GETATTR)
1123				mountreq = TRUE;
1124		}
1125		if (!mountreq) {
1126			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
1127			vfs_unbusy(mp);
1128			goto out;
1129		}
1130	}
1131	error = VFS_FHTOVP(mp, &fhp->fh_fid, LK_EXCLUSIVE, vpp);
1132	if (error) {
1133		/* Make sure the server replies ESTALE to the client. */
1134		error = ESTALE;
1135		vfs_unbusy(mp);
1136		goto out;
1137	}
1138#ifdef MNT_EXNORESPORT
1139	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
1140		saddr = (struct sockaddr_in *)nam;
1141		if ((saddr->sin_family == AF_INET ||
1142		     saddr->sin_family == AF_INET6) &&
1143	/* same code for INET and INET6: sin*_port at same offet */
1144		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
1145			vput(*vpp);
1146			*vpp = NULL;
1147			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
1148			vfs_unbusy(mp);
1149			goto out;
1150		}
1151	}
1152#endif
1153	/*
1154	 * Check/setup credentials.
1155	 */
1156	cred = nfsd->nd_cr;
1157	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
1158		cred->cr_uid = credanon->cr_uid;
1159		crsetgroups(cred, credanon->cr_ngroups, credanon->cr_groups);
1160	}
1161	if (exflags & MNT_EXRDONLY)
1162		*rdonlyp = 1;
1163	else
1164		*rdonlyp = 0;
1165
1166	if (!(flags & NFSRV_FLAG_BUSY))
1167		vfs_unbusy(mp);
1168out:
1169	if (credanon != NULL)
1170		crfree(credanon);
1171
1172	if (error)
1173		VFS_UNLOCK_GIANT(vfslocked);
1174	else
1175		*vfslockedp = vfslocked;
1176	return (error);
1177}
1178
1179
1180/*
1181 * WebNFS: check if a filehandle is a public filehandle. For v3, this
1182 * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
1183 * transformed this to all zeroes in both cases, so check for it.
1184 */
1185int
1186nfs_ispublicfh(fhandle_t *fhp)
1187{
1188	char *cp = (char *)fhp;
1189	int i;
1190
1191	NFSD_LOCK_DONTCARE();
1192
1193	for (i = 0; i < NFSX_V3FH; i++)
1194		if (*cp++ != 0)
1195			return (FALSE);
1196	return (TRUE);
1197}
1198
1199/*
1200 * Map errnos to NFS error numbers. For Version 3 also filter out error
1201 * numbers not specified for the associated procedure.
1202 */
1203int
1204nfsrv_errmap(struct nfsrv_descript *nd, int err)
1205{
1206	const short *defaulterrp, *errp;
1207	int e;
1208
1209
1210	if (nd->nd_flag & ND_NFSV3) {
1211	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
1212		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
1213		while (*++errp) {
1214			if (*errp == err)
1215				return (err);
1216			else if (*errp > err)
1217				break;
1218		}
1219		return ((int)*defaulterrp);
1220	    } else
1221		return (err & 0xffff);
1222	}
1223	e = 0;
1224	if (err <= ELAST)
1225		e = nfsrv_v2errmap[err - 1];
1226	if (e != 0)
1227		return (e);
1228	return (NFSERR_IO);
1229}
1230
1231/*
1232 * Sort the group list in increasing numerical order.
1233 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1234 *  that used to be here.)
1235 */
1236void
1237nfsrvw_sort(gid_t *list, int num)
1238{
1239	int i, j;
1240	gid_t v;
1241
1242	/* Insertion sort. */
1243	for (i = 1; i < num; i++) {
1244		v = list[i];
1245		/* find correct slot for value v, moving others up */
1246		for (j = i; --j >= 0 && v < list[j];)
1247			list[j + 1] = list[j];
1248		list[j + 1] = v;
1249	}
1250}
1251
1252/*
1253 * Helper functions for macros.
1254 */
1255
1256void
1257nfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
1258{
1259	u_int32_t *tl;
1260
1261	if (v3) {
1262		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1263		*tl++ = txdr_unsigned(NFSX_V3FH);
1264		bcopy(f, tl, NFSX_V3FH);
1265	} else {
1266		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
1267		bcopy(f, tl, NFSX_V2FH);
1268	}
1269}
1270
1271void
1272nfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
1273{
1274	u_int32_t *tl;
1275
1276	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1277	*tl++ = nfsrv_nfs_true;
1278	*tl++ = txdr_unsigned(NFSX_V3FH);
1279	bcopy(f, tl, NFSX_V3FH);
1280}
1281
1282int
1283nfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1284{
1285	u_int32_t *tl;
1286
1287	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1288	if (tl == NULL)
1289		return EBADRPC;
1290	*s = fxdr_unsigned(int32_t, *tl);
1291	if (*s > m || *s <= 0)
1292		return EBADRPC;
1293	return 0;
1294}
1295
1296int
1297nfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1298{
1299	u_int32_t *tl;
1300
1301	NFSD_LOCK_DONTCARE();
1302
1303	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1304	if (tl == NULL)
1305		return EBADRPC;
1306	*s = fxdr_unsigned(int32_t, *tl);
1307	if (*s > m)
1308		return NFSERR_NAMETOL;
1309	if (*s <= 0)
1310		return EBADRPC;
1311	return 0;
1312}
1313
1314int
1315nfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1316{
1317	u_int32_t *tl;
1318
1319	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1320	if (tl == NULL)
1321		return EBADRPC;
1322	*s = fxdr_unsigned(int32_t, *tl);
1323	if (*s > m)
1324		return NFSERR_NAMETOL;
1325	if (*s < 0)
1326		return EBADRPC;
1327	return 0;
1328}
1329
1330void
1331nfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1332    char **bp, char **be, caddr_t bpos)
1333{
1334	struct mbuf *nmp;
1335
1336	NFSD_UNLOCK_ASSERT();
1337
1338	if (*bp >= *be) {
1339		if (*mp == mb)
1340			(*mp)->m_len += *bp - bpos;
1341		MGET(nmp, M_WAIT, MT_DATA);
1342		MCLGET(nmp, M_WAIT);
1343		nmp->m_len = NFSMSIZ(nmp);
1344		(*mp)->m_next = nmp;
1345		*mp = nmp;
1346		*bp = mtod(*mp, caddr_t);
1347		*be = *bp + (*mp)->m_len;
1348	}
1349	*tl = (u_int32_t *)*bp;
1350}
1351
1352int
1353nfsm_srvmtofh_xx(fhandle_t *f, int v3, struct mbuf **md, caddr_t *dpos)
1354{
1355	u_int32_t *tl;
1356	int fhlen;
1357
1358	if (v3) {
1359		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1360		if (tl == NULL)
1361			return EBADRPC;
1362		fhlen = fxdr_unsigned(int, *tl);
1363		if (fhlen != 0 && fhlen != NFSX_V3FH)
1364			return EBADRPC;
1365	} else {
1366		fhlen = NFSX_V2FH;
1367	}
1368	if (fhlen != 0) {
1369		tl = nfsm_dissect_xx_nonblock(fhlen, md, dpos);
1370		if (tl == NULL)
1371			return EBADRPC;
1372		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
1373	} else {
1374		bzero((caddr_t)(f), NFSX_V3FH);
1375	}
1376	return 0;
1377}
1378
1379int
1380nfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
1381{
1382	u_int32_t *tl;
1383	int toclient = 0;
1384
1385	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1386	if (tl == NULL)
1387		return EBADRPC;
1388	if (*tl == nfsrv_nfs_true) {
1389		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1390		if (tl == NULL)
1391			return EBADRPC;
1392		(a)->va_mode = nfstov_mode(*tl);
1393	}
1394	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1395	if (tl == NULL)
1396		return EBADRPC;
1397	if (*tl == nfsrv_nfs_true) {
1398		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1399		if (tl == NULL)
1400			return EBADRPC;
1401		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
1402	}
1403	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1404	if (tl == NULL)
1405		return EBADRPC;
1406	if (*tl == nfsrv_nfs_true) {
1407		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1408		if (tl == NULL)
1409			return EBADRPC;
1410		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
1411	}
1412	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1413	if (tl == NULL)
1414		return EBADRPC;
1415	if (*tl == nfsrv_nfs_true) {
1416		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
1417		if (tl == NULL)
1418			return EBADRPC;
1419		(a)->va_size = fxdr_hyper(tl);
1420	}
1421	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1422	if (tl == NULL)
1423		return EBADRPC;
1424	switch (fxdr_unsigned(int, *tl)) {
1425	case NFSV3SATTRTIME_TOCLIENT:
1426		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
1427		if (tl == NULL)
1428			return EBADRPC;
1429		fxdr_nfsv3time(tl, &(a)->va_atime);
1430		toclient = 1;
1431		break;
1432	case NFSV3SATTRTIME_TOSERVER:
1433		getnanotime(&(a)->va_atime);
1434		a->va_vaflags |= VA_UTIMES_NULL;
1435		break;
1436	}
1437	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1438	if (tl == NULL)
1439		return EBADRPC;
1440	switch (fxdr_unsigned(int, *tl)) {
1441	case NFSV3SATTRTIME_TOCLIENT:
1442		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
1443		if (tl == NULL)
1444			return EBADRPC;
1445		fxdr_nfsv3time(tl, &(a)->va_mtime);
1446		a->va_vaflags &= ~VA_UTIMES_NULL;
1447		break;
1448	case NFSV3SATTRTIME_TOSERVER:
1449		getnanotime(&(a)->va_mtime);
1450		if (toclient == 0)
1451			a->va_vaflags |= VA_UTIMES_NULL;
1452		break;
1453	}
1454	return 0;
1455}
1456