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