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