nfs_srvsubs.c revision 123608
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 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 123608 2003-12-17 16:12:01Z jhb $");
41
42/*
43 * These functions support the macros and help fiddle mbuf chains for
44 * the nfs op functions. They do things like create the rpc header and
45 * copy data between mbuf chains and uio lists.
46 */
47
48#include "opt_inet6.h"
49
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/kernel.h>
53#include <sys/bio.h>
54#include <sys/buf.h>
55#include <sys/proc.h>
56#include <sys/mount.h>
57#include <sys/vnode.h>
58#include <sys/namei.h>
59#include <sys/mbuf.h>
60#include <sys/socket.h>
61#include <sys/stat.h>
62#include <sys/malloc.h>
63#include <sys/module.h>
64#include <sys/sysent.h>
65#include <sys/syscall.h>
66#include <sys/sysproto.h>
67
68#include <vm/vm.h>
69#include <vm/vm_object.h>
70#include <vm/vm_extern.h>
71#include <vm/uma.h>
72
73#include <nfs/rpcv2.h>
74#include <nfs/nfsproto.h>
75#include <nfsserver/nfs.h>
76#include <nfs/xdr_subs.h>
77#include <nfsserver/nfsm_subs.h>
78
79#include <netinet/in.h>
80
81/*
82 * Data items converted to xdr at startup, since they are constant
83 * This is kinda hokey, but may save a little time doing byte swaps
84 */
85u_int32_t nfsrv_nfs_xdrneg1;
86u_int32_t nfsrv_rpc_call, nfsrv_rpc_vers, nfsrv_rpc_reply,
87	nfsrv_rpc_msgdenied, nfsrv_rpc_autherr,
88	nfsrv_rpc_mismatch, nfsrv_rpc_auth_unix, nfsrv_rpc_msgaccepted;
89u_int32_t nfsrv_nfs_prog, nfsrv_nfs_true, nfsrv_nfs_false;
90
91/* And other global data */
92static nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK,
93				 NFNON, NFCHR, NFNON };
94#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
95#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
96
97int nfsrv_ticks;
98
99struct nfssvc_sockhead nfssvc_sockhead;
100int nfssvc_sockhead_flag;
101struct nfsd_head nfsd_head;
102int nfsd_head_flag;
103
104static int nfs_prev_nfssvc_sy_narg;
105static sy_call_t *nfs_prev_nfssvc_sy_call;
106
107/*
108 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
109 */
110int 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 */
139int 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 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 short nfsv3err_null[] = {
200	0,
201	0,
202};
203
204static short nfsv3err_getattr[] = {
205	NFSERR_IO,
206	NFSERR_IO,
207	NFSERR_STALE,
208	NFSERR_BADHANDLE,
209	NFSERR_SERVERFAULT,
210	0,
211};
212
213static 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 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 short nfsv3err_access[] = {
243	NFSERR_IO,
244	NFSERR_IO,
245	NFSERR_STALE,
246	NFSERR_BADHANDLE,
247	NFSERR_SERVERFAULT,
248	0,
249};
250
251static 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 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 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 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 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 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 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 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 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 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 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 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 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 short nfsv3err_fsstat[] = {
462	NFSERR_IO,
463	NFSERR_IO,
464	NFSERR_STALE,
465	NFSERR_BADHANDLE,
466	NFSERR_SERVERFAULT,
467	0,
468};
469
470static short nfsv3err_fsinfo[] = {
471	NFSERR_STALE,
472	NFSERR_STALE,
473	NFSERR_BADHANDLE,
474	NFSERR_SERVERFAULT,
475	0,
476};
477
478static short nfsv3err_pathconf[] = {
479	NFSERR_STALE,
480	NFSERR_STALE,
481	NFSERR_BADHANDLE,
482	NFSERR_SERVERFAULT,
483	0,
484};
485
486static short nfsv3err_commit[] = {
487	NFSERR_IO,
488	NFSERR_IO,
489	NFSERR_STALE,
490	NFSERR_BADHANDLE,
491	NFSERR_SERVERFAULT,
492	0,
493};
494
495static 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
527	switch (type) {
528	case MOD_LOAD:
529		nfsrv_rpc_vers = txdr_unsigned(RPC_VER2);
530		nfsrv_rpc_call = txdr_unsigned(RPC_CALL);
531		nfsrv_rpc_reply = txdr_unsigned(RPC_REPLY);
532		nfsrv_rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
533		nfsrv_rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
534		nfsrv_rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
535		nfsrv_rpc_autherr = txdr_unsigned(RPC_AUTHERR);
536		nfsrv_rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
537		nfsrv_nfs_prog = txdr_unsigned(NFS_PROG);
538		nfsrv_nfs_true = txdr_unsigned(TRUE);
539		nfsrv_nfs_false = txdr_unsigned(FALSE);
540		nfsrv_nfs_xdrneg1 = txdr_unsigned(-1);
541		nfsrv_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
542		if (nfsrv_ticks < 1)
543			nfsrv_ticks = 1;
544
545		nfsrv_init(0);		/* Init server data structures */
546		nfsrv_initcache();	/* Init the server request cache */
547
548		nfsrv_timer(0);
549
550		nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
551		sysent[SYS_nfssvc].sy_narg = 2;
552		nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
553		sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
554		break;
555
556		case MOD_UNLOAD:
557
558		untimeout(nfsrv_timer, (void *)NULL, nfsrv_timer_handle);
559		sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
560		sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
561		break;
562	}
563	return 0;
564}
565static moduledata_t nfsserver_mod = {
566	"nfsserver",
567	nfsrv_modevent,
568	NULL,
569};
570DECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
571
572/* So that loader and kldload(2) can find us, wherever we are.. */
573MODULE_VERSION(nfsserver, 1);
574
575/*
576 * Set up nameidata for a lookup() call and do it.
577 *
578 * If pubflag is set, this call is done for a lookup operation on the
579 * public filehandle. In that case we allow crossing mountpoints and
580 * absolute pathnames. However, the caller is expected to check that
581 * the lookup result is within the public fs, and deny access if
582 * it is not.
583 *
584 * nfs_namei() clears out garbage fields that namei() might leave garbage.
585 * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
586 * error occurs but the parent was not requested.
587 *
588 * dirp may be set whether an error is returned or not, and must be
589 * released by the caller.
590 */
591int
592nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
593    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
594    caddr_t *dposp, struct vnode **retdirp, int v3, struct vattr *retdirattrp,
595    int *retdirattr_retp, struct thread *td, int pubflag)
596{
597	int i, rem;
598	struct mbuf *md;
599	char *fromcp, *tocp, *cp;
600	struct iovec aiov;
601	struct uio auio;
602	struct vnode *dp;
603	int error, rdonly, linklen;
604	struct componentname *cnp = &ndp->ni_cnd;
605	int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0;
606
607	*retdirp = NULL;
608	cnp->cn_flags |= NOMACCHECK;
609	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
610
611	/*
612	 * Copy the name from the mbuf list to ndp->ni_pnbuf
613	 * and set the various ndp fields appropriately.
614	 */
615	fromcp = *dposp;
616	tocp = cnp->cn_pnbuf;
617	md = *mdp;
618	rem = mtod(md, caddr_t) + md->m_len - fromcp;
619	for (i = 0; i < len; i++) {
620		while (rem == 0) {
621			md = md->m_next;
622			if (md == NULL) {
623				error = EBADRPC;
624				goto out;
625			}
626			fromcp = mtod(md, caddr_t);
627			rem = md->m_len;
628		}
629		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
630			error = EACCES;
631			goto out;
632		}
633		*tocp++ = *fromcp++;
634		rem--;
635	}
636	*tocp = '\0';
637	*mdp = md;
638	*dposp = fromcp;
639	len = nfsm_rndup(len)-len;
640	if (len > 0) {
641		if (rem >= len)
642			*dposp += len;
643		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
644			goto out;
645	}
646
647	/*
648	 * Extract and set starting directory.
649	 */
650	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
651	    nam, &rdonly, pubflag);
652	if (error)
653		goto out;
654	if (dp->v_type != VDIR) {
655		vrele(dp);
656		error = ENOTDIR;
657		goto out;
658	}
659
660	if (rdonly)
661		cnp->cn_flags |= RDONLY;
662
663	/*
664	 * Set return directory.  Reference to dp is implicitly transfered
665	 * to the returned pointer
666	 */
667	*retdirp = dp;
668	if (v3) {
669		vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td);
670		*retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
671			ndp->ni_cnd.cn_cred, td);
672		VOP_UNLOCK(dp, 0, td);
673	}
674
675	if (pubflag) {
676		/*
677		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
678		 * and the 'native path' indicator.
679		 */
680		cp = uma_zalloc(namei_zone, M_WAITOK);
681		fromcp = cnp->cn_pnbuf;
682		tocp = cp;
683		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
684			switch ((unsigned char)*fromcp) {
685			case WEBNFS_NATIVE_CHAR:
686				/*
687				 * 'Native' path for us is the same
688				 * as a path according to the NFS spec,
689				 * just skip the escape char.
690				 */
691				fromcp++;
692				break;
693			/*
694			 * More may be added in the future, range 0x80-0xff
695			 */
696			default:
697				error = EIO;
698				uma_zfree(namei_zone, cp);
699				goto out;
700			}
701		}
702		/*
703		 * Translate the '%' escapes, URL-style.
704		 */
705		while (*fromcp != '\0') {
706			if (*fromcp == WEBNFS_ESC_CHAR) {
707				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
708					fromcp++;
709					*tocp++ = HEXSTRTOI(fromcp);
710					fromcp += 2;
711					continue;
712				} else {
713					error = ENOENT;
714					uma_zfree(namei_zone, cp);
715					goto out;
716				}
717			} else
718				*tocp++ = *fromcp++;
719		}
720		*tocp = '\0';
721		uma_zfree(namei_zone, cnp->cn_pnbuf);
722		cnp->cn_pnbuf = cp;
723	}
724
725	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
726	ndp->ni_segflg = UIO_SYSSPACE;
727
728	if (pubflag) {
729		ndp->ni_rootdir = rootvnode;
730		ndp->ni_loopcnt = 0;
731		if (cnp->cn_pnbuf[0] == '/')
732			dp = rootvnode;
733	} else {
734		cnp->cn_flags |= NOCROSSMOUNT;
735	}
736
737	/*
738	 * Initialize for scan, set ni_startdir and bump ref on dp again
739	 * because lookup() will dereference ni_startdir.
740	 */
741
742	cnp->cn_thread = td;
743	VREF(dp);
744	ndp->ni_startdir = dp;
745
746	if (!lockleaf)
747		cnp->cn_flags |= LOCKLEAF;
748	for (;;) {
749		cnp->cn_nameptr = cnp->cn_pnbuf;
750		/*
751		 * Call lookup() to do the real work.  If an error occurs,
752		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
753		 * we do not have to dereference anything before returning.
754		 * In either case ni_startdir will be dereferenced and NULLed
755		 * out.
756		 */
757		error = lookup(ndp);
758		if (error)
759			break;
760
761		/*
762		 * Check for encountering a symbolic link.  Trivial
763		 * termination occurs if no symlink encountered.
764		 * Note: zfree is safe because error is 0, so we will
765		 * not zfree it again when we break.
766		 */
767		if ((cnp->cn_flags & ISSYMLINK) == 0) {
768			nfsrv_object_create(ndp->ni_vp);
769			if (cnp->cn_flags & (SAVENAME | SAVESTART))
770				cnp->cn_flags |= HASBUF;
771			else
772				uma_zfree(namei_zone, cnp->cn_pnbuf);
773			if (ndp->ni_vp && !lockleaf)
774				VOP_UNLOCK(ndp->ni_vp, 0, td);
775			break;
776		}
777
778		/*
779		 * Validate symlink
780		 */
781		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
782			VOP_UNLOCK(ndp->ni_dvp, 0, td);
783		if (!pubflag) {
784			error = EINVAL;
785			goto badlink2;
786		}
787
788		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
789			error = ELOOP;
790			goto badlink2;
791		}
792		if (ndp->ni_pathlen > 1)
793			cp = uma_zalloc(namei_zone, M_WAITOK);
794		else
795			cp = cnp->cn_pnbuf;
796		aiov.iov_base = cp;
797		aiov.iov_len = MAXPATHLEN;
798		auio.uio_iov = &aiov;
799		auio.uio_iovcnt = 1;
800		auio.uio_offset = 0;
801		auio.uio_rw = UIO_READ;
802		auio.uio_segflg = UIO_SYSSPACE;
803		auio.uio_td = NULL;
804		auio.uio_resid = MAXPATHLEN;
805		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
806		if (error) {
807		badlink1:
808			if (ndp->ni_pathlen > 1)
809				uma_zfree(namei_zone, cp);
810		badlink2:
811			vrele(ndp->ni_dvp);
812			vput(ndp->ni_vp);
813			break;
814		}
815		linklen = MAXPATHLEN - auio.uio_resid;
816		if (linklen == 0) {
817			error = ENOENT;
818			goto badlink1;
819		}
820		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
821			error = ENAMETOOLONG;
822			goto badlink1;
823		}
824
825		/*
826		 * Adjust or replace path
827		 */
828		if (ndp->ni_pathlen > 1) {
829			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
830			uma_zfree(namei_zone, cnp->cn_pnbuf);
831			cnp->cn_pnbuf = cp;
832		} else
833			cnp->cn_pnbuf[linklen] = '\0';
834		ndp->ni_pathlen += linklen;
835
836		/*
837		 * Cleanup refs for next loop and check if root directory
838		 * should replace current directory.  Normally ni_dvp
839		 * becomes the new base directory and is cleaned up when
840		 * we loop.  Explicitly null pointers after invalidation
841		 * to clarify operation.
842		 */
843		vput(ndp->ni_vp);
844		ndp->ni_vp = NULL;
845
846		if (cnp->cn_pnbuf[0] == '/') {
847			vrele(ndp->ni_dvp);
848			ndp->ni_dvp = ndp->ni_rootdir;
849			VREF(ndp->ni_dvp);
850		}
851		ndp->ni_startdir = ndp->ni_dvp;
852		ndp->ni_dvp = NULL;
853	}
854	if (!lockleaf)
855		cnp->cn_flags &= ~LOCKLEAF;
856
857	/*
858	 * nfs_namei() guarentees that fields will not contain garbage
859	 * whether an error occurs or not.  This allows the caller to track
860	 * cleanup state trivially.
861	 */
862out:
863	if (error) {
864		uma_zfree(namei_zone, cnp->cn_pnbuf);
865		ndp->ni_vp = NULL;
866		ndp->ni_dvp = NULL;
867		ndp->ni_startdir = NULL;
868		cnp->cn_flags &= ~HASBUF;
869	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
870		ndp->ni_dvp = NULL;
871	}
872	return (error);
873}
874
875/*
876 * A fiddled version of m_adj() that ensures null fill to a long
877 * boundary and only trims off the back end
878 */
879void
880nfsm_adj(struct mbuf *mp, int len, int nul)
881{
882	struct mbuf *m;
883	int count, i;
884	char *cp;
885
886	/*
887	 * Trim from tail.  Scan the mbuf chain,
888	 * calculating its length and finding the last mbuf.
889	 * If the adjustment only affects this mbuf, then just
890	 * adjust and return.  Otherwise, rescan and truncate
891	 * after the remaining size.
892	 */
893	count = 0;
894	m = mp;
895	for (;;) {
896		count += m->m_len;
897		if (m->m_next == NULL)
898			break;
899		m = m->m_next;
900	}
901	if (m->m_len > len) {
902		m->m_len -= len;
903		if (nul > 0) {
904			cp = mtod(m, caddr_t)+m->m_len-nul;
905			for (i = 0; i < nul; i++)
906				*cp++ = '\0';
907		}
908		return;
909	}
910	count -= len;
911	if (count < 0)
912		count = 0;
913	/*
914	 * Correct length for chain is "count".
915	 * Find the mbuf with last data, adjust its length,
916	 * and toss data from remaining mbufs on chain.
917	 */
918	for (m = mp; m; m = m->m_next) {
919		if (m->m_len >= count) {
920			m->m_len = count;
921			if (nul > 0) {
922				cp = mtod(m, caddr_t)+m->m_len-nul;
923				for (i = 0; i < nul; i++)
924					*cp++ = '\0';
925			}
926			break;
927		}
928		count -= m->m_len;
929	}
930	for (m = m->m_next;m;m = m->m_next)
931		m->m_len = 0;
932}
933
934/*
935 * Make these functions instead of macros, so that the kernel text size
936 * doesn't get too big...
937 */
938void
939nfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
940    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
941    struct mbuf **mbp, char **bposp)
942{
943	struct mbuf *mb = *mbp;
944	char *bpos = *bposp;
945	u_int32_t *tl;
946
947	if (before_ret) {
948		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
949		*tl = nfsrv_nfs_false;
950	} else {
951		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
952		*tl++ = nfsrv_nfs_true;
953		txdr_hyper(before_vap->va_size, tl);
954		tl += 2;
955		txdr_nfsv3time(&(before_vap->va_mtime), tl);
956		tl += 2;
957		txdr_nfsv3time(&(before_vap->va_ctime), tl);
958	}
959	*bposp = bpos;
960	*mbp = mb;
961	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
962}
963
964void
965nfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
966    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
967{
968	struct mbuf *mb = *mbp;
969	char *bpos = *bposp;
970	u_int32_t *tl;
971	struct nfs_fattr *fp;
972
973	if (after_ret) {
974		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
975		*tl = nfsrv_nfs_false;
976	} else {
977		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
978		*tl++ = nfsrv_nfs_true;
979		fp = (struct nfs_fattr *)tl;
980		nfsm_srvfattr(nfsd, after_vap, fp);
981	}
982	*mbp = mb;
983	*bposp = bpos;
984}
985
986void
987nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
988    struct nfs_fattr *fp)
989{
990
991	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
992	fp->fa_uid = txdr_unsigned(vap->va_uid);
993	fp->fa_gid = txdr_unsigned(vap->va_gid);
994	if (nfsd->nd_flag & ND_NFSV3) {
995		fp->fa_type = vtonfsv3_type(vap->va_type);
996		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
997		txdr_hyper(vap->va_size, &fp->fa3_size);
998		txdr_hyper(vap->va_bytes, &fp->fa3_used);
999		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
1000		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
1001		fp->fa3_fsid.nfsuquad[0] = 0;
1002		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
1003		fp->fa3_fileid.nfsuquad[0] = 0;
1004		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
1005		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
1006		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
1007		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
1008	} else {
1009		fp->fa_type = vtonfsv2_type(vap->va_type);
1010		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
1011		fp->fa2_size = txdr_unsigned(vap->va_size);
1012		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
1013		if (vap->va_type == VFIFO)
1014			fp->fa2_rdev = 0xffffffff;
1015		else
1016			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
1017		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
1018		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
1019		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
1020		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
1021		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
1022		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
1023	}
1024}
1025
1026/*
1027 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
1028 * 	- look up fsid in mount list (if not found ret error)
1029 *	- get vp and export rights by calling VFS_FHTOVP()
1030 *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
1031 *	- if not lockflag unlock it with VOP_UNLOCK()
1032 */
1033int
1034nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
1035    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
1036    int *rdonlyp, int pubflag)
1037{
1038	struct thread *td = curthread; /* XXX */
1039	struct mount *mp;
1040	int i;
1041	struct ucred *credanon;
1042	int error, exflags;
1043#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
1044	struct sockaddr_int *saddr;
1045#endif
1046
1047	*vpp = NULL;
1048
1049	if (nfs_ispublicfh(fhp)) {
1050		if (!pubflag || !nfs_pub.np_valid)
1051			return (ESTALE);
1052		fhp = &nfs_pub.np_handle;
1053	}
1054
1055	mp = vfs_getvfs(&fhp->fh_fsid);
1056	if (!mp)
1057		return (ESTALE);
1058	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
1059	if (error)
1060		return (error);
1061	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
1062	if (error)
1063		return (error);
1064#ifdef MNT_EXNORESPORT
1065	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
1066		saddr = (struct sockaddr_in *)nam;
1067		if ((saddr->sin_family == AF_INET ||
1068		     saddr->sin_family == AF_INET6) &&
1069	/* same code for INET and INET6: sin*_port at same offet */
1070		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
1071			vput(*vpp);
1072			*vpp = NULL;
1073			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
1074		}
1075	}
1076#endif
1077	/*
1078	 * Check/setup credentials.
1079	 */
1080	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
1081		cred->cr_uid = credanon->cr_uid;
1082		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
1083			cred->cr_groups[i] = credanon->cr_groups[i];
1084		cred->cr_ngroups = i;
1085	}
1086	if (exflags & MNT_EXRDONLY)
1087		*rdonlyp = 1;
1088	else
1089		*rdonlyp = 0;
1090
1091	nfsrv_object_create(*vpp);
1092
1093	if (!lockflag)
1094		VOP_UNLOCK(*vpp, 0, td);
1095	return (0);
1096}
1097
1098
1099/*
1100 * WebNFS: check if a filehandle is a public filehandle. For v3, this
1101 * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
1102 * transformed this to all zeroes in both cases, so check for it.
1103 */
1104int
1105nfs_ispublicfh(fhandle_t *fhp)
1106{
1107	char *cp = (char *)fhp;
1108	int i;
1109
1110	for (i = 0; i < NFSX_V3FH; i++)
1111		if (*cp++ != 0)
1112			return (FALSE);
1113	return (TRUE);
1114}
1115
1116/*
1117 * This function compares two net addresses by family and returns TRUE
1118 * if they are the same host.
1119 * If there is any doubt, return FALSE.
1120 * The AF_INET family is handled as a special case so that address mbufs
1121 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
1122 */
1123int
1124netaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
1125{
1126	struct sockaddr_in *inetaddr;
1127
1128	switch (family) {
1129	case AF_INET:
1130		inetaddr = (struct sockaddr_in *)nam;
1131		if (inetaddr->sin_family == AF_INET &&
1132		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
1133			return (1);
1134		break;
1135#ifdef INET6
1136	case AF_INET6:
1137	{
1138		register struct sockaddr_in6 *inet6addr1, *inet6addr2;
1139
1140		inet6addr1 = (struct sockaddr_in6 *)nam;
1141		inet6addr2 = (struct sockaddr_in6 *)haddr->had_nam;
1142	/* XXX - should test sin6_scope_id ? */
1143		if (inet6addr1->sin6_family == AF_INET6 &&
1144		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
1145				       &inet6addr2->sin6_addr))
1146			return (1);
1147		break;
1148	}
1149#endif
1150	default:
1151		break;
1152	};
1153	return (0);
1154}
1155
1156/*
1157 * Map errnos to NFS error numbers. For Version 3 also filter out error
1158 * numbers not specified for the associated procedure.
1159 */
1160int
1161nfsrv_errmap(struct nfsrv_descript *nd, int err)
1162{
1163	short *defaulterrp, *errp;
1164	int e;
1165
1166	if (nd->nd_flag & ND_NFSV3) {
1167	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
1168		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
1169		while (*++errp) {
1170			if (*errp == err)
1171				return (err);
1172			else if (*errp > err)
1173				break;
1174		}
1175		return ((int)*defaulterrp);
1176	    } else
1177		return (err & 0xffff);
1178	}
1179	e = 0;
1180	if (err <= ELAST)
1181		e = nfsrv_v2errmap[err - 1];
1182	if (e != 0)
1183		return (e);
1184	return (NFSERR_IO);
1185}
1186
1187int
1188nfsrv_object_create(struct vnode *vp)
1189{
1190
1191	if (vp == NULL || vp->v_type != VREG)
1192		return (1);
1193	return (vfs_object_create(vp, curthread, curthread->td_ucred));
1194}
1195
1196/*
1197 * Sort the group list in increasing numerical order.
1198 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1199 *  that used to be here.)
1200 */
1201void
1202nfsrvw_sort(gid_t *list, int num)
1203{
1204	int i, j;
1205	gid_t v;
1206
1207	/* Insertion sort. */
1208	for (i = 1; i < num; i++) {
1209		v = list[i];
1210		/* find correct slot for value v, moving others up */
1211		for (j = i; --j >= 0 && v < list[j];)
1212			list[j + 1] = list[j];
1213		list[j + 1] = v;
1214	}
1215}
1216
1217/*
1218 * copy credentials making sure that the result can be compared with bcmp().
1219 */
1220void
1221nfsrv_setcred(struct ucred *incred, struct ucred *outcred)
1222{
1223	int i;
1224
1225	bzero((caddr_t)outcred, sizeof (struct ucred));
1226	outcred->cr_ref = 1;
1227	outcred->cr_uid = incred->cr_uid;
1228	outcred->cr_ngroups = incred->cr_ngroups;
1229	for (i = 0; i < incred->cr_ngroups; i++)
1230		outcred->cr_groups[i] = incred->cr_groups[i];
1231	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
1232}
1233
1234/*
1235 * Helper functions for macros.
1236 */
1237
1238void
1239nfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
1240{
1241	u_int32_t *tl;
1242
1243	if (v3) {
1244		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1245		*tl++ = txdr_unsigned(NFSX_V3FH);
1246		bcopy(f, tl, NFSX_V3FH);
1247	} else {
1248		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
1249		bcopy(f, tl, NFSX_V2FH);
1250	}
1251}
1252
1253void
1254nfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
1255{
1256	u_int32_t *tl;
1257
1258	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1259	*tl++ = nfsrv_nfs_true;
1260	*tl++ = txdr_unsigned(NFSX_V3FH);
1261	bcopy(f, tl, NFSX_V3FH);
1262}
1263
1264int
1265nfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1266{
1267	u_int32_t *tl;
1268
1269	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1270	if (tl == NULL)
1271		return EBADRPC;
1272	*s = fxdr_unsigned(int32_t, *tl);
1273	if (*s > m || *s <= 0)
1274		return EBADRPC;
1275	return 0;
1276}
1277
1278int
1279nfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1280{
1281	u_int32_t *tl;
1282
1283	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1284	if (tl == NULL)
1285		return EBADRPC;
1286	*s = fxdr_unsigned(int32_t, *tl);
1287	if (*s > m)
1288		return NFSERR_NAMETOL;
1289	if (*s <= 0)
1290		return EBADRPC;
1291	return 0;
1292}
1293
1294void
1295nfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1296    char **bp, char **be, caddr_t bpos)
1297{
1298	struct mbuf *nmp;
1299
1300	if (*bp >= *be) {
1301		if (*mp == mb)
1302			(*mp)->m_len += *bp - bpos;
1303		MGET(nmp, M_TRYWAIT, MT_DATA);
1304		MCLGET(nmp, M_TRYWAIT);
1305		nmp->m_len = NFSMSIZ(nmp);
1306		(*mp)->m_next = nmp;
1307		*mp = nmp;
1308		*bp = mtod(*mp, caddr_t);
1309		*be = *bp + (*mp)->m_len;
1310	}
1311	*tl = (u_int32_t *)*bp;
1312}
1313
1314int
1315nfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md,
1316    caddr_t *dpos)
1317{
1318	u_int32_t *tl;
1319	int fhlen;
1320
1321	if (nfsd->nd_flag & ND_NFSV3) {
1322		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1323		if (tl == NULL)
1324			return EBADRPC;
1325		fhlen = fxdr_unsigned(int, *tl);
1326		if (fhlen != 0 && fhlen != NFSX_V3FH)
1327			return EBADRPC;
1328	} else {
1329		fhlen = NFSX_V2FH;
1330	}
1331	if (fhlen != 0) {
1332		tl = nfsm_dissect_xx(fhlen, md, dpos);
1333		if (tl == NULL)
1334			return EBADRPC;
1335		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
1336	} else {
1337		bzero((caddr_t)(f), NFSX_V3FH);
1338	}
1339	return 0;
1340}
1341
1342int
1343nfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
1344{
1345	u_int32_t *tl;
1346
1347	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1348	if (tl == NULL)
1349		return EBADRPC;
1350	if (*tl == nfsrv_nfs_true) {
1351		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1352		if (tl == NULL)
1353			return EBADRPC;
1354		(a)->va_mode = nfstov_mode(*tl);
1355	}
1356	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1357	if (tl == NULL)
1358		return EBADRPC;
1359	if (*tl == nfsrv_nfs_true) {
1360		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1361		if (tl == NULL)
1362			return EBADRPC;
1363		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
1364	}
1365	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1366	if (tl == NULL)
1367		return EBADRPC;
1368	if (*tl == nfsrv_nfs_true) {
1369		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1370		if (tl == NULL)
1371			return EBADRPC;
1372		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
1373	}
1374	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1375	if (tl == NULL)
1376		return EBADRPC;
1377	if (*tl == nfsrv_nfs_true) {
1378		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
1379		if (tl == NULL)
1380			return EBADRPC;
1381		(a)->va_size = fxdr_hyper(tl);
1382	}
1383	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1384	if (tl == NULL)
1385		return EBADRPC;
1386	switch (fxdr_unsigned(int, *tl)) {
1387	case NFSV3SATTRTIME_TOCLIENT:
1388		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
1389		if (tl == NULL)
1390			return EBADRPC;
1391		fxdr_nfsv3time(tl, &(a)->va_atime);
1392		break;
1393	case NFSV3SATTRTIME_TOSERVER:
1394		getnanotime(&(a)->va_atime);
1395		break;
1396	}
1397	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
1398	if (tl == NULL)
1399		return EBADRPC;
1400	switch (fxdr_unsigned(int, *tl)) {
1401	case NFSV3SATTRTIME_TOCLIENT:
1402		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
1403		if (tl == NULL)
1404			return EBADRPC;
1405		fxdr_nfsv3time(tl, &(a)->va_mtime);
1406		break;
1407	case NFSV3SATTRTIME_TOSERVER:
1408		getnanotime(&(a)->va_mtime);
1409		break;
1410	}
1411	return 0;
1412}
1413