nfs_srvsubs.c revision 83651
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 83651 2001-09-18 23:32:09Z peter $
38 */
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 83651 2001-09-18 23:32:09Z 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/sysent.h>
63#include <sys/syscall.h>
64#include <sys/sysproto.h>
65
66#include <vm/vm.h>
67#include <vm/vm_object.h>
68#include <vm/vm_extern.h>
69#include <vm/vm_zone.h>
70
71#include <nfs/rpcv2.h>
72#include <nfs/nfsproto.h>
73#include <nfsserver/nfs.h>
74#include <nfs/xdr_subs.h>
75#include <nfsserver/nfsm_subs.h>
76
77#include <netinet/in.h>
78
79/*
80 * Data items converted to xdr at startup, since they are constant
81 * This is kinda hokey, but may save a little time doing byte swaps
82 */
83u_int32_t nfs_xdrneg1;
84u_int32_t rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
85	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
86u_int32_t nfs_prog, nfs_true, nfs_false;
87
88/* And other global data */
89static nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK,
90				 NFNON, NFCHR, NFNON };
91#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
92#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
93
94int nfs_ticks;
95
96struct nfssvc_sockhead nfssvc_sockhead;
97int nfssvc_sockhead_flag;
98struct nfsd_head nfsd_head;
99int nfsd_head_flag;
100
101static int nfs_prev_nfssvc_sy_narg;
102static sy_call_t *nfs_prev_nfssvc_sy_call;
103
104/*
105 * Mapping of old NFS Version 2 RPC numbers to generic numbers.
106 */
107int nfsv3_procid[NFS_NPROCS] = {
108	NFSPROC_NULL,
109	NFSPROC_GETATTR,
110	NFSPROC_SETATTR,
111	NFSPROC_NOOP,
112	NFSPROC_LOOKUP,
113	NFSPROC_READLINK,
114	NFSPROC_READ,
115	NFSPROC_NOOP,
116	NFSPROC_WRITE,
117	NFSPROC_CREATE,
118	NFSPROC_REMOVE,
119	NFSPROC_RENAME,
120	NFSPROC_LINK,
121	NFSPROC_SYMLINK,
122	NFSPROC_MKDIR,
123	NFSPROC_RMDIR,
124	NFSPROC_READDIR,
125	NFSPROC_FSSTAT,
126	NFSPROC_NOOP,
127	NFSPROC_NOOP,
128	NFSPROC_NOOP,
129	NFSPROC_NOOP,
130	NFSPROC_NOOP,
131};
132
133/*
134 * and the reverse mapping from generic to Version 2 procedure numbers
135 */
136int nfsrvv2_procid[NFS_NPROCS] = {
137	NFSV2PROC_NULL,
138	NFSV2PROC_GETATTR,
139	NFSV2PROC_SETATTR,
140	NFSV2PROC_LOOKUP,
141	NFSV2PROC_NOOP,
142	NFSV2PROC_READLINK,
143	NFSV2PROC_READ,
144	NFSV2PROC_WRITE,
145	NFSV2PROC_CREATE,
146	NFSV2PROC_MKDIR,
147	NFSV2PROC_SYMLINK,
148	NFSV2PROC_CREATE,
149	NFSV2PROC_REMOVE,
150	NFSV2PROC_RMDIR,
151	NFSV2PROC_RENAME,
152	NFSV2PROC_LINK,
153	NFSV2PROC_READDIR,
154	NFSV2PROC_NOOP,
155	NFSV2PROC_STATFS,
156	NFSV2PROC_NOOP,
157	NFSV2PROC_NOOP,
158	NFSV2PROC_NOOP,
159	NFSV2PROC_NOOP,
160};
161
162/*
163 * Maps errno values to nfs error numbers.
164 * Use NFSERR_IO as the catch all for ones not specifically defined in
165 * RFC 1094.
166 */
167static u_char nfsrv_v2errmap[ELAST] = {
168  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
169  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
170  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
171  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
172  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
173  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
174  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
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_NAMETOL,	NFSERR_IO,	NFSERR_IO,
181  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
182  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
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 /* << Last is 86 */
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
524nfs_init(void *dummy __unused)
525{
526
527	rpc_vers = txdr_unsigned(RPC_VER2);
528	rpc_call = txdr_unsigned(RPC_CALL);
529	rpc_reply = txdr_unsigned(RPC_REPLY);
530	rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
531	rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
532	rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
533	rpc_autherr = txdr_unsigned(RPC_AUTHERR);
534	rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
535	nfs_prog = txdr_unsigned(NFS_PROG);
536	nfs_true = txdr_unsigned(TRUE);
537	nfs_false = txdr_unsigned(FALSE);
538	nfs_xdrneg1 = txdr_unsigned(-1);
539	nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
540	if (nfs_ticks < 1)
541		nfs_ticks = 1;
542
543	nfsrv_init(0);			/* Init server data structures */
544	nfsrv_initcache();		/* Init the server request cache */
545
546	nfsrv_timer(0);
547
548	nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
549	sysent[SYS_nfssvc].sy_narg = 2;
550	nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
551	sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
552
553	return (0);
554}
555SYSINIT(nfs, SI_SUB_VFS, SI_ORDER_ANY, nfs_init, NULL);
556
557static int
558nfs_uninit(void *dummy __unused)
559{
560
561	untimeout(nfsrv_timer, (void *)NULL, nfsrv_timer_handle);
562	sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
563	sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
564	return (0);
565}
566SYSUNINIT(nfs, SI_SUB_VFS, SI_ORDER_ANY, nfs_uninit, NULL);
567
568/*
569 * Set up nameidata for a lookup() call and do it.
570 *
571 * If pubflag is set, this call is done for a lookup operation on the
572 * public filehandle. In that case we allow crossing mountpoints and
573 * absolute pathnames. However, the caller is expected to check that
574 * the lookup result is within the public fs, and deny access if
575 * it is not.
576 *
577 * nfs_namei() clears out garbage fields that namei() might leave garbage.
578 * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
579 * error occurs but the parent was not requested.
580 *
581 * dirp may be set whether an error is returned or not, and must be
582 * released by the caller.
583 */
584int
585nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
586    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
587    caddr_t *dposp, struct vnode **retdirp, struct thread *td, int pubflag)
588{
589	int i, rem;
590	struct mbuf *md;
591	char *fromcp, *tocp, *cp;
592	struct iovec aiov;
593	struct uio auio;
594	struct vnode *dp;
595	int error, rdonly, linklen;
596	struct componentname *cnp = &ndp->ni_cnd;
597
598	*retdirp = (struct vnode *)0;
599	cnp->cn_pnbuf = zalloc(namei_zone);
600
601	/*
602	 * Copy the name from the mbuf list to ndp->ni_pnbuf
603	 * and set the various ndp fields appropriately.
604	 */
605	fromcp = *dposp;
606	tocp = cnp->cn_pnbuf;
607	md = *mdp;
608	rem = mtod(md, caddr_t) + md->m_len - fromcp;
609	for (i = 0; i < len; i++) {
610		while (rem == 0) {
611			md = md->m_next;
612			if (md == NULL) {
613				error = EBADRPC;
614				goto out;
615			}
616			fromcp = mtod(md, caddr_t);
617			rem = md->m_len;
618		}
619		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
620			error = EACCES;
621			goto out;
622		}
623		*tocp++ = *fromcp++;
624		rem--;
625	}
626	*tocp = '\0';
627	*mdp = md;
628	*dposp = fromcp;
629	len = nfsm_rndup(len)-len;
630	if (len > 0) {
631		if (rem >= len)
632			*dposp += len;
633		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
634			goto out;
635	}
636
637	/*
638	 * Extract and set starting directory.
639	 */
640	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
641	    nam, &rdonly, pubflag);
642	if (error)
643		goto out;
644	if (dp->v_type != VDIR) {
645		vrele(dp);
646		error = ENOTDIR;
647		goto out;
648	}
649
650	if (rdonly)
651		cnp->cn_flags |= RDONLY;
652
653	/*
654	 * Set return directory.  Reference to dp is implicitly transfered
655	 * to the returned pointer
656	 */
657	*retdirp = dp;
658
659	if (pubflag) {
660		/*
661		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
662		 * and the 'native path' indicator.
663		 */
664		cp = zalloc(namei_zone);
665		fromcp = cnp->cn_pnbuf;
666		tocp = cp;
667		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
668			switch ((unsigned char)*fromcp) {
669			case WEBNFS_NATIVE_CHAR:
670				/*
671				 * 'Native' path for us is the same
672				 * as a path according to the NFS spec,
673				 * just skip the escape char.
674				 */
675				fromcp++;
676				break;
677			/*
678			 * More may be added in the future, range 0x80-0xff
679			 */
680			default:
681				error = EIO;
682				zfree(namei_zone, cp);
683				goto out;
684			}
685		}
686		/*
687		 * Translate the '%' escapes, URL-style.
688		 */
689		while (*fromcp != '\0') {
690			if (*fromcp == WEBNFS_ESC_CHAR) {
691				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
692					fromcp++;
693					*tocp++ = HEXSTRTOI(fromcp);
694					fromcp += 2;
695					continue;
696				} else {
697					error = ENOENT;
698					zfree(namei_zone, cp);
699					goto out;
700				}
701			} else
702				*tocp++ = *fromcp++;
703		}
704		*tocp = '\0';
705		zfree(namei_zone, cnp->cn_pnbuf);
706		cnp->cn_pnbuf = cp;
707	}
708
709	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
710	ndp->ni_segflg = UIO_SYSSPACE;
711
712	if (pubflag) {
713		ndp->ni_rootdir = rootvnode;
714		ndp->ni_loopcnt = 0;
715		if (cnp->cn_pnbuf[0] == '/')
716			dp = rootvnode;
717	} else {
718		cnp->cn_flags |= NOCROSSMOUNT;
719	}
720
721	/*
722	 * Initialize for scan, set ni_startdir and bump ref on dp again
723	 * becuase lookup() will dereference ni_startdir.
724	 */
725
726	cnp->cn_thread = td;
727	VREF(dp);
728	ndp->ni_startdir = dp;
729
730	for (;;) {
731		cnp->cn_nameptr = cnp->cn_pnbuf;
732		/*
733		 * Call lookup() to do the real work.  If an error occurs,
734		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
735		 * we do not have to dereference anything before returning.
736		 * In either case ni_startdir will be dereferenced and NULLed
737		 * out.
738		 */
739		error = lookup(ndp);
740		if (error)
741			break;
742
743		/*
744		 * Check for encountering a symbolic link.  Trivial
745		 * termination occurs if no symlink encountered.
746		 * Note: zfree is safe because error is 0, so we will
747		 * not zfree it again when we break.
748		 */
749		if ((cnp->cn_flags & ISSYMLINK) == 0) {
750			nfsrv_object_create(ndp->ni_vp);
751			if (cnp->cn_flags & (SAVENAME | SAVESTART))
752				cnp->cn_flags |= HASBUF;
753			else
754				zfree(namei_zone, cnp->cn_pnbuf);
755			break;
756		}
757
758		/*
759		 * Validate symlink
760		 */
761		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
762			VOP_UNLOCK(ndp->ni_dvp, 0, td);
763		if (!pubflag) {
764			error = EINVAL;
765			goto badlink2;
766		}
767
768		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
769			error = ELOOP;
770			goto badlink2;
771		}
772		if (ndp->ni_pathlen > 1)
773			cp = zalloc(namei_zone);
774		else
775			cp = cnp->cn_pnbuf;
776		aiov.iov_base = cp;
777		aiov.iov_len = MAXPATHLEN;
778		auio.uio_iov = &aiov;
779		auio.uio_iovcnt = 1;
780		auio.uio_offset = 0;
781		auio.uio_rw = UIO_READ;
782		auio.uio_segflg = UIO_SYSSPACE;
783		auio.uio_td = (struct thread *)0;
784		auio.uio_resid = MAXPATHLEN;
785		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
786		if (error) {
787		badlink1:
788			if (ndp->ni_pathlen > 1)
789				zfree(namei_zone, cp);
790		badlink2:
791			vrele(ndp->ni_dvp);
792			vput(ndp->ni_vp);
793			break;
794		}
795		linklen = MAXPATHLEN - auio.uio_resid;
796		if (linklen == 0) {
797			error = ENOENT;
798			goto badlink1;
799		}
800		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
801			error = ENAMETOOLONG;
802			goto badlink1;
803		}
804
805		/*
806		 * Adjust or replace path
807		 */
808		if (ndp->ni_pathlen > 1) {
809			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
810			zfree(namei_zone, cnp->cn_pnbuf);
811			cnp->cn_pnbuf = cp;
812		} else
813			cnp->cn_pnbuf[linklen] = '\0';
814		ndp->ni_pathlen += linklen;
815
816		/*
817		 * Cleanup refs for next loop and check if root directory
818		 * should replace current directory.  Normally ni_dvp
819		 * becomes the new base directory and is cleaned up when
820		 * we loop.  Explicitly null pointers after invalidation
821		 * to clarify operation.
822		 */
823		vput(ndp->ni_vp);
824		ndp->ni_vp = NULL;
825
826		if (cnp->cn_pnbuf[0] == '/') {
827			vrele(ndp->ni_dvp);
828			ndp->ni_dvp = ndp->ni_rootdir;
829			VREF(ndp->ni_dvp);
830		}
831		ndp->ni_startdir = ndp->ni_dvp;
832		ndp->ni_dvp = NULL;
833	}
834
835	/*
836	 * nfs_namei() guarentees that fields will not contain garbage
837	 * whether an error occurs or not.  This allows the caller to track
838	 * cleanup state trivially.
839	 */
840out:
841	if (error) {
842		zfree(namei_zone, cnp->cn_pnbuf);
843		ndp->ni_vp = NULL;
844		ndp->ni_dvp = NULL;
845		ndp->ni_startdir = NULL;
846		cnp->cn_flags &= ~HASBUF;
847	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
848		ndp->ni_dvp = NULL;
849	}
850	return (error);
851}
852
853/*
854 * A fiddled version of m_adj() that ensures null fill to a long
855 * boundary and only trims off the back end
856 */
857void
858nfsm_adj(struct mbuf *mp, int len, int nul)
859{
860	struct mbuf *m;
861	int count, i;
862	char *cp;
863
864	/*
865	 * Trim from tail.  Scan the mbuf chain,
866	 * calculating its length and finding the last mbuf.
867	 * If the adjustment only affects this mbuf, then just
868	 * adjust and return.  Otherwise, rescan and truncate
869	 * after the remaining size.
870	 */
871	count = 0;
872	m = mp;
873	for (;;) {
874		count += m->m_len;
875		if (m->m_next == (struct mbuf *)0)
876			break;
877		m = m->m_next;
878	}
879	if (m->m_len > len) {
880		m->m_len -= len;
881		if (nul > 0) {
882			cp = mtod(m, caddr_t)+m->m_len-nul;
883			for (i = 0; i < nul; i++)
884				*cp++ = '\0';
885		}
886		return;
887	}
888	count -= len;
889	if (count < 0)
890		count = 0;
891	/*
892	 * Correct length for chain is "count".
893	 * Find the mbuf with last data, adjust its length,
894	 * and toss data from remaining mbufs on chain.
895	 */
896	for (m = mp; m; m = m->m_next) {
897		if (m->m_len >= count) {
898			m->m_len = count;
899			if (nul > 0) {
900				cp = mtod(m, caddr_t)+m->m_len-nul;
901				for (i = 0; i < nul; i++)
902					*cp++ = '\0';
903			}
904			break;
905		}
906		count -= m->m_len;
907	}
908	for (m = m->m_next;m;m = m->m_next)
909		m->m_len = 0;
910}
911
912/*
913 * Make these functions instead of macros, so that the kernel text size
914 * doesn't get too big...
915 */
916void
917nfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
918    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
919    struct mbuf **mbp, char **bposp)
920{
921	struct mbuf *mb = *mbp;
922	char *bpos = *bposp;
923	u_int32_t *tl;
924
925	if (before_ret) {
926		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
927		*tl = nfs_false;
928	} else {
929		nfsm_build(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
930		*tl++ = nfs_true;
931		txdr_hyper(before_vap->va_size, tl);
932		tl += 2;
933		txdr_nfsv3time(&(before_vap->va_mtime), tl);
934		tl += 2;
935		txdr_nfsv3time(&(before_vap->va_ctime), tl);
936	}
937	*bposp = bpos;
938	*mbp = mb;
939	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
940}
941
942void
943nfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
944    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
945{
946	struct mbuf *mb = *mbp;
947	char *bpos = *bposp;
948	u_int32_t *tl;
949	struct nfs_fattr *fp;
950
951	if (after_ret) {
952		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
953		*tl = nfs_false;
954	} else {
955		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
956		*tl++ = nfs_true;
957		fp = (struct nfs_fattr *)tl;
958		nfsm_srvfattr(nfsd, after_vap, fp);
959	}
960	*mbp = mb;
961	*bposp = bpos;
962}
963
964void
965nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
966    struct nfs_fattr *fp)
967{
968
969	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
970	fp->fa_uid = txdr_unsigned(vap->va_uid);
971	fp->fa_gid = txdr_unsigned(vap->va_gid);
972	if (nfsd->nd_flag & ND_NFSV3) {
973		fp->fa_type = vtonfsv3_type(vap->va_type);
974		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
975		txdr_hyper(vap->va_size, &fp->fa3_size);
976		txdr_hyper(vap->va_bytes, &fp->fa3_used);
977		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
978		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
979		fp->fa3_fsid.nfsuquad[0] = 0;
980		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
981		fp->fa3_fileid.nfsuquad[0] = 0;
982		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
983		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
984		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
985		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
986	} else {
987		fp->fa_type = vtonfsv2_type(vap->va_type);
988		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
989		fp->fa2_size = txdr_unsigned(vap->va_size);
990		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
991		if (vap->va_type == VFIFO)
992			fp->fa2_rdev = 0xffffffff;
993		else
994			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
995		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
996		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
997		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
998		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
999		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
1000		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
1001	}
1002}
1003
1004/*
1005 * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
1006 * 	- look up fsid in mount list (if not found ret error)
1007 *	- get vp and export rights by calling VFS_FHTOVP()
1008 *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
1009 *	- if not lockflag unlock it with VOP_UNLOCK()
1010 */
1011int
1012nfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
1013    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
1014    int *rdonlyp, int pubflag)
1015{
1016	struct thread *td = curthread; /* XXX */
1017	struct mount *mp;
1018	int i;
1019	struct ucred *credanon;
1020	int error, exflags;
1021#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
1022	struct sockaddr_int *saddr;
1023#endif
1024
1025	*vpp = (struct vnode *)0;
1026
1027	if (nfs_ispublicfh(fhp)) {
1028		if (!pubflag || !nfs_pub.np_valid)
1029			return (ESTALE);
1030		fhp = &nfs_pub.np_handle;
1031	}
1032
1033	mp = vfs_getvfs(&fhp->fh_fsid);
1034	if (!mp)
1035		return (ESTALE);
1036	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
1037	if (error)
1038		return (error);
1039	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
1040	if (error)
1041		return (error);
1042#ifdef MNT_EXNORESPORT
1043	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
1044		saddr = (struct sockaddr_in *)nam;
1045		if (saddr->sin_family == AF_INET &&
1046		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
1047			vput(*vpp);
1048			*vpp = NULL;
1049			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
1050		}
1051	}
1052#endif
1053	/*
1054	 * Check/setup credentials.
1055	 */
1056	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
1057		cred->cr_uid = credanon->cr_uid;
1058		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
1059			cred->cr_groups[i] = credanon->cr_groups[i];
1060		cred->cr_ngroups = i;
1061	}
1062	if (exflags & MNT_EXRDONLY)
1063		*rdonlyp = 1;
1064	else
1065		*rdonlyp = 0;
1066
1067	nfsrv_object_create(*vpp);
1068
1069	if (!lockflag)
1070		VOP_UNLOCK(*vpp, 0, td);
1071	return (0);
1072}
1073
1074
1075/*
1076 * WebNFS: check if a filehandle is a public filehandle. For v3, this
1077 * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
1078 * transformed this to all zeroes in both cases, so check for it.
1079 */
1080int
1081nfs_ispublicfh(fhandle_t *fhp)
1082{
1083	char *cp = (char *)fhp;
1084	int i;
1085
1086	for (i = 0; i < NFSX_V3FH; i++)
1087		if (*cp++ != 0)
1088			return (FALSE);
1089	return (TRUE);
1090}
1091
1092/*
1093 * This function compares two net addresses by family and returns TRUE
1094 * if they are the same host.
1095 * If there is any doubt, return FALSE.
1096 * The AF_INET family is handled as a special case so that address mbufs
1097 * don't need to be saved to store "struct in_addr", which is only 4 bytes.
1098 */
1099int
1100netaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
1101{
1102	struct sockaddr_in *inetaddr;
1103
1104	switch (family) {
1105	case AF_INET:
1106		inetaddr = (struct sockaddr_in *)nam;
1107		if (inetaddr->sin_family == AF_INET &&
1108		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
1109			return (1);
1110		break;
1111	default:
1112		break;
1113	};
1114	return (0);
1115}
1116
1117/*
1118 * Map errnos to NFS error numbers. For Version 3 also filter out error
1119 * numbers not specified for the associated procedure.
1120 */
1121int
1122nfsrv_errmap(struct nfsrv_descript *nd, int err)
1123{
1124	short *defaulterrp, *errp;
1125
1126	if (nd->nd_flag & ND_NFSV3) {
1127	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
1128		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
1129		while (*++errp) {
1130			if (*errp == err)
1131				return (err);
1132			else if (*errp > err)
1133				break;
1134		}
1135		return ((int)*defaulterrp);
1136	    } else
1137		return (err & 0xffff);
1138	}
1139	if (err <= ELAST)
1140		return ((int)nfsrv_v2errmap[err - 1]);
1141	return (NFSERR_IO);
1142}
1143
1144int
1145nfsrv_object_create(struct vnode *vp)
1146{
1147
1148	if (vp == NULL || vp->v_type != VREG)
1149		return (1);
1150	return (vfs_object_create(vp, curthread,
1151			  curthread ? curthread->td_proc->p_ucred : NULL));
1152}
1153
1154/*
1155 * Sort the group list in increasing numerical order.
1156 * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
1157 *  that used to be here.)
1158 */
1159void
1160nfsrvw_sort(gid_t *list, int num)
1161{
1162	int i, j;
1163	gid_t v;
1164
1165	/* Insertion sort. */
1166	for (i = 1; i < num; i++) {
1167		v = list[i];
1168		/* find correct slot for value v, moving others up */
1169		for (j = i; --j >= 0 && v < list[j];)
1170			list[j + 1] = list[j];
1171		list[j + 1] = v;
1172	}
1173}
1174
1175/*
1176 * copy credentials making sure that the result can be compared with bcmp().
1177 */
1178void
1179nfsrv_setcred(struct ucred *incred, struct ucred *outcred)
1180{
1181	int i;
1182
1183	bzero((caddr_t)outcred, sizeof (struct ucred));
1184	outcred->cr_ref = 1;
1185	outcred->cr_uid = incred->cr_uid;
1186	outcred->cr_ngroups = incred->cr_ngroups;
1187	for (i = 0; i < incred->cr_ngroups; i++)
1188		outcred->cr_groups[i] = incred->cr_groups[i];
1189	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
1190}
1191
1192/*
1193 * Helper functions for macros.
1194 */
1195
1196void
1197nfsm_srvfhtom_xx(fhandle_t *f, int v3,
1198    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
1199{
1200	u_int32_t *cp;
1201
1202	if (v3) {
1203		nfsm_build_xx((void **)tl, NFSX_UNSIGNED + NFSX_V3FH, mb,
1204				bpos);
1205		*(*tl)++ = txdr_unsigned(NFSX_V3FH);
1206		bcopy(f, (*tl), NFSX_V3FH);
1207	} else {
1208		nfsm_build_xx((void **)&cp, NFSX_V2FH, mb, bpos);
1209		bcopy(f, cp, NFSX_V2FH);
1210	}
1211}
1212
1213void
1214nfsm_srvpostop_fh_xx(fhandle_t *f,
1215    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
1216{
1217	nfsm_build_xx((void **)tl, 2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
1218	*(*tl)++ = nfs_true;
1219	*(*tl)++ = txdr_unsigned(NFSX_V3FH);
1220	bcopy(f, (*tl), NFSX_V3FH);
1221}
1222
1223int
1224nfsm_srvstrsiz_xx(int *s, int m,
1225    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
1226{
1227	int ret;
1228
1229	ret = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1230	if (ret)
1231		return ret;
1232	*s = fxdr_unsigned(int32_t, **tl);
1233	if (*s > m || *s <= 0)
1234		return EBADRPC;
1235	return 0;
1236}
1237
1238int
1239nfsm_srvnamesiz_xx(int *s,
1240    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
1241{
1242	int ret;
1243
1244	ret = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1245	if (ret)
1246		return ret;
1247	*s = fxdr_unsigned(int32_t, **tl);
1248	if (*s > NFS_MAXNAMLEN)
1249		return NFSERR_NAMETOL;
1250	if (*s <= 0)
1251		return EBADRPC;
1252	return 0;
1253}
1254
1255void
1256nfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1257    char **bp, char **be, caddr_t bpos)
1258{
1259	struct mbuf *nmp;
1260
1261	if (*bp >= *be) {
1262		if (*mp == mb)
1263			(*mp)->m_len += *bp - bpos;
1264		MGET(nmp, M_TRYWAIT, MT_DATA);
1265		MCLGET(nmp, M_TRYWAIT);
1266		nmp->m_len = NFSMSIZ(nmp);
1267		(*mp)->m_next = nmp;
1268		*mp = nmp;
1269		*bp = mtod(*mp, caddr_t);
1270		*be = *bp + (*mp)->m_len;
1271	}
1272	*tl = (u_int32_t *)*bp;
1273}
1274
1275int
1276nfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd,
1277    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
1278{
1279	int error;
1280	int fhlen;
1281
1282	if (nfsd->nd_flag & ND_NFSV3) {
1283		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1284		if (error)
1285			return error;
1286		fhlen = fxdr_unsigned(int, **tl);
1287		if (fhlen != 0 && fhlen != NFSX_V3FH)
1288			return EBADRPC;
1289	} else {
1290		fhlen = NFSX_V2FH;
1291	}
1292	if (fhlen != 0) {
1293		error = nfsm_dissect_xx((void **)tl, fhlen, md, dpos);
1294		if (error)
1295			return error;
1296		bcopy((caddr_t)*tl, (caddr_t)(f), fhlen);
1297	} else {
1298		bzero((caddr_t)(f), NFSX_V3FH);
1299	}
1300	return 0;
1301}
1302
1303int
1304nfsm_srvsattr_xx(struct vattr *a,
1305    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
1306{
1307	int error = 0;
1308
1309	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1310	if (error)
1311		goto bugout;
1312	if (**tl == nfs_true) {
1313		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1314		if (error)
1315			goto bugout;
1316		(a)->va_mode = nfstov_mode(**tl);
1317	}
1318	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1319	if (error)
1320		goto bugout;
1321	if (**tl == nfs_true) {
1322		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1323		if (error)
1324			goto bugout;
1325		(a)->va_uid = fxdr_unsigned(uid_t, **tl);
1326	}
1327	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1328	if (error)
1329		goto bugout;
1330	if (**tl == nfs_true) {
1331		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1332		if (error)
1333			goto bugout;
1334		(a)->va_gid = fxdr_unsigned(gid_t, **tl);
1335	}
1336	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1337	if (error)
1338		goto bugout;
1339	if (**tl == nfs_true) {
1340		error = nfsm_dissect_xx((void **)tl, 2 * NFSX_UNSIGNED,
1341		    md, dpos);
1342		if (error)
1343			goto bugout;
1344		(a)->va_size = fxdr_hyper(*tl);
1345	}
1346	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1347	if (error)
1348		goto bugout;
1349	switch (fxdr_unsigned(int, **tl)) {
1350	case NFSV3SATTRTIME_TOCLIENT:
1351		error = nfsm_dissect_xx((void **)tl, 2 * NFSX_UNSIGNED,
1352		    md, dpos);
1353		if (error)
1354			goto bugout;
1355		fxdr_nfsv3time(*tl, &(a)->va_atime);
1356		break;
1357	case NFSV3SATTRTIME_TOSERVER:
1358		getnanotime(&(a)->va_atime);
1359		break;
1360	}
1361	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
1362	if (error)
1363		goto bugout;
1364	switch (fxdr_unsigned(int, **tl)) {
1365	case NFSV3SATTRTIME_TOCLIENT:
1366		error = nfsm_dissect_xx((void **)tl, 2 * NFSX_UNSIGNED,
1367		    md, dpos);
1368		if (error)
1369			goto bugout;
1370		fxdr_nfsv3time(*tl, &(a)->va_mtime);
1371		break;
1372	case NFSV3SATTRTIME_TOSERVER:
1373		getnanotime(&(a)->va_mtime);
1374		break;
1375	}
1376	return 0;
1377
1378bugout:
1379	return error;
1380}
1381
1382void
1383nfs_rephead_xx(int s, struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
1384    int error, struct mbuf **mrq, struct mbuf **mb,
1385    struct mbuf **mreq, struct mbuf **mrep, caddr_t *bpos)
1386{
1387
1388	nfs_rephead(s, nfsd, slp, error, mrq, mb, bpos);
1389	if (*mrep != NULL) {
1390		m_freem(*mrep);
1391		*mrep = NULL;
1392	}
1393	*mreq = *mrq;
1394}
1395