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