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