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