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