nfs_srvsubs.c revision 48274
1284741Sdavidcs/*
2284741Sdavidcs * Copyright (c) 1989, 1993
3284741Sdavidcs *	The Regents of the University of California.  All rights reserved.
4284741Sdavidcs *
5284741Sdavidcs * This code is derived from software contributed to Berkeley by
6284741Sdavidcs * Rick Macklem at The University of Guelph.
7284741Sdavidcs *
8284741Sdavidcs * Redistribution and use in source and binary forms, with or without
9284741Sdavidcs * modification, are permitted provided that the following conditions
10284741Sdavidcs * are met:
11284741Sdavidcs * 1. Redistributions of source code must retain the above copyright
12284741Sdavidcs *    notice, this list of conditions and the following disclaimer.
13284741Sdavidcs * 2. Redistributions in binary form must reproduce the above copyright
14284741Sdavidcs *    notice, this list of conditions and the following disclaimer in the
15284741Sdavidcs *    documentation and/or other materials provided with the distribution.
16284741Sdavidcs * 3. All advertising materials mentioning features or use of this software
17284741Sdavidcs *    must display the following acknowledgement:
18284741Sdavidcs *	This product includes software developed by the University of
19284741Sdavidcs *	California, Berkeley and its contributors.
20284741Sdavidcs * 4. Neither the name of the University nor the names of its contributors
21284741Sdavidcs *    may be used to endorse or promote products derived from this software
22284741Sdavidcs *    without specific prior written permission.
23284741Sdavidcs *
24284741Sdavidcs * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25284741Sdavidcs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26284741Sdavidcs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27284741Sdavidcs * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28284741Sdavidcs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29284741Sdavidcs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30284741Sdavidcs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31284741Sdavidcs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32284741Sdavidcs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33284741Sdavidcs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34284741Sdavidcs * SUCH DAMAGE.
35284741Sdavidcs *
36284741Sdavidcs *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
37284741Sdavidcs * $Id: nfs_subs.c,v 1.77 1999/06/26 02:46:30 mckusick Exp $
38284741Sdavidcs */
39284741Sdavidcs
40284741Sdavidcs/*
41284741Sdavidcs * These functions support the macros and help fiddle mbuf chains for
42284741Sdavidcs * the nfs op functions. They do things like create the rpc header and
43284741Sdavidcs * copy data between mbuf chains and uio lists.
44284741Sdavidcs */
45284741Sdavidcs#include <sys/param.h>
46284741Sdavidcs#include <sys/systm.h>
47284741Sdavidcs#include <sys/kernel.h>
48284741Sdavidcs#include <sys/buf.h>
49284741Sdavidcs#include <sys/proc.h>
50284741Sdavidcs#include <sys/mount.h>
51284741Sdavidcs#include <sys/vnode.h>
52284741Sdavidcs#include <sys/namei.h>
53284741Sdavidcs#include <sys/mbuf.h>
54284741Sdavidcs#include <sys/socket.h>
55284741Sdavidcs#include <sys/stat.h>
56284741Sdavidcs#include <sys/malloc.h>
57284741Sdavidcs#include <sys/sysent.h>
58284741Sdavidcs#include <sys/syscall.h>
59284741Sdavidcs
60284741Sdavidcs#include <vm/vm.h>
61284741Sdavidcs#include <vm/vm_object.h>
62284741Sdavidcs#include <vm/vm_extern.h>
63284741Sdavidcs#include <vm/vm_zone.h>
64284741Sdavidcs
65284741Sdavidcs#include <nfs/rpcv2.h>
66284741Sdavidcs#include <nfs/nfsproto.h>
67284741Sdavidcs#include <nfs/nfs.h>
68284741Sdavidcs#include <nfs/nfsnode.h>
69284741Sdavidcs#include <nfs/xdr_subs.h>
70284741Sdavidcs#include <nfs/nfsm_subs.h>
71284741Sdavidcs#include <nfs/nfsmount.h>
72284741Sdavidcs#include <nfs/nqnfs.h>
73284741Sdavidcs#include <nfs/nfsrtt.h>
74284741Sdavidcs
75284741Sdavidcs#include <miscfs/specfs/specdev.h>
76284741Sdavidcs
77284741Sdavidcs#include <netinet/in.h>
78284741Sdavidcs#ifdef ISO
79284741Sdavidcs#include <netiso/iso.h>
80284741Sdavidcs#endif
81284741Sdavidcs
82284741Sdavidcs/*
83284741Sdavidcs * Data items converted to xdr at startup, since they are constant
84284741Sdavidcs * This is kinda hokey, but may save a little time doing byte swaps
85284741Sdavidcs */
86284741Sdavidcsu_int32_t nfs_xdrneg1;
87284741Sdavidcsu_int32_t rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
88284741Sdavidcs	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted,
89284741Sdavidcs	rpc_auth_kerb;
90284741Sdavidcsu_int32_t nfs_prog, nqnfs_prog, nfs_true, nfs_false;
91284741Sdavidcs
92284741Sdavidcs/* And other global data */
93284741Sdavidcsstatic u_int32_t nfs_xid = 0;
94284741Sdavidcsstatic enum vtype nv2tov_type[8]= {
95284741Sdavidcs	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON,  VNON
96284741Sdavidcs};
97284741Sdavidcsenum vtype nv3tov_type[8]= {
98284741Sdavidcs	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
99284741Sdavidcs};
100284741Sdavidcs
101284741Sdavidcsint nfs_ticks;
102284741Sdavidcsint nfs_pbuf_freecnt = -1;	/* start out unlimited */
103284741Sdavidcs
104284741Sdavidcsstruct nfs_reqq nfs_reqq;
105284741Sdavidcsstruct nfssvc_sockhead nfssvc_sockhead;
106284741Sdavidcsint nfssvc_sockhead_flag;
107284741Sdavidcsstruct nfsd_head nfsd_head;
108284741Sdavidcsint nfsd_head_flag;
109284741Sdavidcsstruct nfs_bufq nfs_bufq;
110284741Sdavidcsstruct nqtimerhead nqtimerhead;
111284741Sdavidcsstruct nqfhhashhead *nqfhhashtbl;
112284741Sdavidcsu_long nqfhhash;
113284741Sdavidcs
114284741Sdavidcsstatic void (*nfs_prev_lease_updatetime) __P((int));
115284741Sdavidcsstatic int nfs_prev_nfssvc_sy_narg;
116284741Sdavidcsstatic sy_call_t *nfs_prev_nfssvc_sy_call;
117284741Sdavidcs
118284741Sdavidcs#ifndef NFS_NOSERVER
119284741Sdavidcs
120284741Sdavidcsstatic vop_t *nfs_prev_vop_lease_check;
121284741Sdavidcsstatic int nfs_prev_getfh_sy_narg;
122284741Sdavidcsstatic sy_call_t *nfs_prev_getfh_sy_call;
123284741Sdavidcs
124284741Sdavidcs/*
125284741Sdavidcs * Mapping of old NFS Version 2 RPC numbers to generic numbers.
126284741Sdavidcs */
127284741Sdavidcsint nfsv3_procid[NFS_NPROCS] = {
128284741Sdavidcs	NFSPROC_NULL,
129284741Sdavidcs	NFSPROC_GETATTR,
130284741Sdavidcs	NFSPROC_SETATTR,
131284741Sdavidcs	NFSPROC_NOOP,
132284741Sdavidcs	NFSPROC_LOOKUP,
133284741Sdavidcs	NFSPROC_READLINK,
134284741Sdavidcs	NFSPROC_READ,
135284741Sdavidcs	NFSPROC_NOOP,
136284741Sdavidcs	NFSPROC_WRITE,
137284741Sdavidcs	NFSPROC_CREATE,
138284741Sdavidcs	NFSPROC_REMOVE,
139284741Sdavidcs	NFSPROC_RENAME,
140284741Sdavidcs	NFSPROC_LINK,
141284741Sdavidcs	NFSPROC_SYMLINK,
142284741Sdavidcs	NFSPROC_MKDIR,
143284741Sdavidcs	NFSPROC_RMDIR,
144284741Sdavidcs	NFSPROC_READDIR,
145284741Sdavidcs	NFSPROC_FSSTAT,
146284741Sdavidcs	NFSPROC_NOOP,
147284741Sdavidcs	NFSPROC_NOOP,
148284741Sdavidcs	NFSPROC_NOOP,
149284741Sdavidcs	NFSPROC_NOOP,
150284741Sdavidcs	NFSPROC_NOOP,
151284741Sdavidcs	NFSPROC_NOOP,
152284741Sdavidcs	NFSPROC_NOOP,
153284741Sdavidcs	NFSPROC_NOOP
154284741Sdavidcs};
155284741Sdavidcs
156284741Sdavidcs#endif /* NFS_NOSERVER */
157284741Sdavidcs/*
158284741Sdavidcs * and the reverse mapping from generic to Version 2 procedure numbers
159284741Sdavidcs */
160284741Sdavidcsint nfsv2_procid[NFS_NPROCS] = {
161284741Sdavidcs	NFSV2PROC_NULL,
162284741Sdavidcs	NFSV2PROC_GETATTR,
163284741Sdavidcs	NFSV2PROC_SETATTR,
164284741Sdavidcs	NFSV2PROC_LOOKUP,
165284741Sdavidcs	NFSV2PROC_NOOP,
166284741Sdavidcs	NFSV2PROC_READLINK,
167284741Sdavidcs	NFSV2PROC_READ,
168284741Sdavidcs	NFSV2PROC_WRITE,
169284741Sdavidcs	NFSV2PROC_CREATE,
170284741Sdavidcs	NFSV2PROC_MKDIR,
171284741Sdavidcs	NFSV2PROC_SYMLINK,
172284741Sdavidcs	NFSV2PROC_CREATE,
173284741Sdavidcs	NFSV2PROC_REMOVE,
174284741Sdavidcs	NFSV2PROC_RMDIR,
175284741Sdavidcs	NFSV2PROC_RENAME,
176284741Sdavidcs	NFSV2PROC_LINK,
177284741Sdavidcs	NFSV2PROC_READDIR,
178284741Sdavidcs	NFSV2PROC_NOOP,
179284741Sdavidcs	NFSV2PROC_STATFS,
180284741Sdavidcs	NFSV2PROC_NOOP,
181284741Sdavidcs	NFSV2PROC_NOOP,
182284741Sdavidcs	NFSV2PROC_NOOP,
183284741Sdavidcs	NFSV2PROC_NOOP,
184284741Sdavidcs	NFSV2PROC_NOOP,
185284741Sdavidcs	NFSV2PROC_NOOP,
186284741Sdavidcs	NFSV2PROC_NOOP,
187284741Sdavidcs};
188284741Sdavidcs
189284741Sdavidcs#ifndef NFS_NOSERVER
190284741Sdavidcs/*
191284741Sdavidcs * Maps errno values to nfs error numbers.
192284741Sdavidcs * Use NFSERR_IO as the catch all for ones not specifically defined in
193284741Sdavidcs * RFC 1094.
194284741Sdavidcs */
195284741Sdavidcsstatic u_char nfsrv_v2errmap[ELAST] = {
196284741Sdavidcs  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
197284741Sdavidcs  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
198284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
199284741Sdavidcs  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
200284741Sdavidcs  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
201284741Sdavidcs  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
202284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
203284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
204284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
205284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
206284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
207284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
208284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_NAMETOL,	NFSERR_IO,	NFSERR_IO,
209284741Sdavidcs  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
210284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
211284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
212284741Sdavidcs  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
213284741Sdavidcs  NFSERR_IO /* << Last is 86 */
214284741Sdavidcs};
215284741Sdavidcs
216284741Sdavidcs/*
217284741Sdavidcs * Maps errno values to nfs error numbers.
218284741Sdavidcs * Although it is not obvious whether or not NFS clients really care if
219284741Sdavidcs * a returned error value is in the specified list for the procedure, the
220284741Sdavidcs * safest thing to do is filter them appropriately. For Version 2, the
221284741Sdavidcs * X/Open XNFS document is the only specification that defines error values
222284741Sdavidcs * for each RPC (The RFC simply lists all possible error values for all RPCs),
223284741Sdavidcs * so I have decided to not do this for Version 2.
224284741Sdavidcs * The first entry is the default error return and the rest are the valid
225284741Sdavidcs * errors for that RPC in increasing numeric order.
226284741Sdavidcs */
227284741Sdavidcsstatic short nfsv3err_null[] = {
228284741Sdavidcs	0,
229284741Sdavidcs	0,
230284741Sdavidcs};
231284741Sdavidcs
232284741Sdavidcsstatic short nfsv3err_getattr[] = {
233284741Sdavidcs	NFSERR_IO,
234284741Sdavidcs	NFSERR_IO,
235284741Sdavidcs	NFSERR_STALE,
236284741Sdavidcs	NFSERR_BADHANDLE,
237284741Sdavidcs	NFSERR_SERVERFAULT,
238284741Sdavidcs	0,
239284741Sdavidcs};
240284741Sdavidcs
241284741Sdavidcsstatic short nfsv3err_setattr[] = {
242284741Sdavidcs	NFSERR_IO,
243284741Sdavidcs	NFSERR_PERM,
244284741Sdavidcs	NFSERR_IO,
245284741Sdavidcs	NFSERR_ACCES,
246284741Sdavidcs	NFSERR_INVAL,
247284741Sdavidcs	NFSERR_NOSPC,
248284741Sdavidcs	NFSERR_ROFS,
249284741Sdavidcs	NFSERR_DQUOT,
250284741Sdavidcs	NFSERR_STALE,
251284741Sdavidcs	NFSERR_BADHANDLE,
252284741Sdavidcs	NFSERR_NOT_SYNC,
253284741Sdavidcs	NFSERR_SERVERFAULT,
254284741Sdavidcs	0,
255284741Sdavidcs};
256284741Sdavidcs
257284741Sdavidcsstatic short nfsv3err_lookup[] = {
258284741Sdavidcs	NFSERR_IO,
259284741Sdavidcs	NFSERR_NOENT,
260284741Sdavidcs	NFSERR_IO,
261284741Sdavidcs	NFSERR_ACCES,
262284741Sdavidcs	NFSERR_NOTDIR,
263284741Sdavidcs	NFSERR_NAMETOL,
264284741Sdavidcs	NFSERR_STALE,
265284741Sdavidcs	NFSERR_BADHANDLE,
266284741Sdavidcs	NFSERR_SERVERFAULT,
267284741Sdavidcs	0,
268284741Sdavidcs};
269284741Sdavidcs
270284741Sdavidcsstatic short nfsv3err_access[] = {
271284741Sdavidcs	NFSERR_IO,
272284741Sdavidcs	NFSERR_IO,
273284741Sdavidcs	NFSERR_STALE,
274284741Sdavidcs	NFSERR_BADHANDLE,
275284741Sdavidcs	NFSERR_SERVERFAULT,
276284741Sdavidcs	0,
277284741Sdavidcs};
278284741Sdavidcs
279284741Sdavidcsstatic short nfsv3err_readlink[] = {
280284741Sdavidcs	NFSERR_IO,
281284741Sdavidcs	NFSERR_IO,
282284741Sdavidcs	NFSERR_ACCES,
283284741Sdavidcs	NFSERR_INVAL,
284284741Sdavidcs	NFSERR_STALE,
285284741Sdavidcs	NFSERR_BADHANDLE,
286284741Sdavidcs	NFSERR_NOTSUPP,
287284741Sdavidcs	NFSERR_SERVERFAULT,
288284741Sdavidcs	0,
289284741Sdavidcs};
290284741Sdavidcs
291284741Sdavidcsstatic short nfsv3err_read[] = {
292284741Sdavidcs	NFSERR_IO,
293284741Sdavidcs	NFSERR_IO,
294284741Sdavidcs	NFSERR_NXIO,
295284741Sdavidcs	NFSERR_ACCES,
296284741Sdavidcs	NFSERR_INVAL,
297284741Sdavidcs	NFSERR_STALE,
298284741Sdavidcs	NFSERR_BADHANDLE,
299284741Sdavidcs	NFSERR_SERVERFAULT,
300284741Sdavidcs	0,
301284741Sdavidcs};
302284741Sdavidcs
303284741Sdavidcsstatic short nfsv3err_write[] = {
304284741Sdavidcs	NFSERR_IO,
305284741Sdavidcs	NFSERR_IO,
306284741Sdavidcs	NFSERR_ACCES,
307284741Sdavidcs	NFSERR_INVAL,
308284741Sdavidcs	NFSERR_FBIG,
309284741Sdavidcs	NFSERR_NOSPC,
310284741Sdavidcs	NFSERR_ROFS,
311284741Sdavidcs	NFSERR_DQUOT,
312284741Sdavidcs	NFSERR_STALE,
313284741Sdavidcs	NFSERR_BADHANDLE,
314284741Sdavidcs	NFSERR_SERVERFAULT,
315284741Sdavidcs	0,
316284741Sdavidcs};
317284741Sdavidcs
318284741Sdavidcsstatic short nfsv3err_create[] = {
319284741Sdavidcs	NFSERR_IO,
320284741Sdavidcs	NFSERR_IO,
321284741Sdavidcs	NFSERR_ACCES,
322284741Sdavidcs	NFSERR_EXIST,
323284741Sdavidcs	NFSERR_NOTDIR,
324284741Sdavidcs	NFSERR_NOSPC,
325284741Sdavidcs	NFSERR_ROFS,
326284741Sdavidcs	NFSERR_NAMETOL,
327284741Sdavidcs	NFSERR_DQUOT,
328284741Sdavidcs	NFSERR_STALE,
329284741Sdavidcs	NFSERR_BADHANDLE,
330284741Sdavidcs	NFSERR_NOTSUPP,
331284741Sdavidcs	NFSERR_SERVERFAULT,
332284741Sdavidcs	0,
333284741Sdavidcs};
334284741Sdavidcs
335284741Sdavidcsstatic short nfsv3err_mkdir[] = {
336284741Sdavidcs	NFSERR_IO,
337284741Sdavidcs	NFSERR_IO,
338284741Sdavidcs	NFSERR_ACCES,
339284741Sdavidcs	NFSERR_EXIST,
340284741Sdavidcs	NFSERR_NOTDIR,
341284741Sdavidcs	NFSERR_NOSPC,
342284741Sdavidcs	NFSERR_ROFS,
343284741Sdavidcs	NFSERR_NAMETOL,
344284741Sdavidcs	NFSERR_DQUOT,
345284741Sdavidcs	NFSERR_STALE,
346284741Sdavidcs	NFSERR_BADHANDLE,
347284741Sdavidcs	NFSERR_NOTSUPP,
348284741Sdavidcs	NFSERR_SERVERFAULT,
349284741Sdavidcs	0,
350284741Sdavidcs};
351284741Sdavidcs
352284741Sdavidcsstatic short nfsv3err_symlink[] = {
353284741Sdavidcs	NFSERR_IO,
354284741Sdavidcs	NFSERR_IO,
355284741Sdavidcs	NFSERR_ACCES,
356284741Sdavidcs	NFSERR_EXIST,
357284741Sdavidcs	NFSERR_NOTDIR,
358284741Sdavidcs	NFSERR_NOSPC,
359284741Sdavidcs	NFSERR_ROFS,
360284741Sdavidcs	NFSERR_NAMETOL,
361284741Sdavidcs	NFSERR_DQUOT,
362284741Sdavidcs	NFSERR_STALE,
363284741Sdavidcs	NFSERR_BADHANDLE,
364284741Sdavidcs	NFSERR_NOTSUPP,
365284741Sdavidcs	NFSERR_SERVERFAULT,
366284741Sdavidcs	0,
367284741Sdavidcs};
368284741Sdavidcs
369284741Sdavidcsstatic short nfsv3err_mknod[] = {
370284741Sdavidcs	NFSERR_IO,
371284741Sdavidcs	NFSERR_IO,
372284741Sdavidcs	NFSERR_ACCES,
373284741Sdavidcs	NFSERR_EXIST,
374284741Sdavidcs	NFSERR_NOTDIR,
375284741Sdavidcs	NFSERR_NOSPC,
376284741Sdavidcs	NFSERR_ROFS,
377284741Sdavidcs	NFSERR_NAMETOL,
378284741Sdavidcs	NFSERR_DQUOT,
379284741Sdavidcs	NFSERR_STALE,
380284741Sdavidcs	NFSERR_BADHANDLE,
381284741Sdavidcs	NFSERR_NOTSUPP,
382284741Sdavidcs	NFSERR_SERVERFAULT,
383284741Sdavidcs	NFSERR_BADTYPE,
384284741Sdavidcs	0,
385284741Sdavidcs};
386284741Sdavidcs
387284741Sdavidcsstatic short nfsv3err_remove[] = {
388284741Sdavidcs	NFSERR_IO,
389284741Sdavidcs	NFSERR_NOENT,
390284741Sdavidcs	NFSERR_IO,
391284741Sdavidcs	NFSERR_ACCES,
392284741Sdavidcs	NFSERR_NOTDIR,
393284741Sdavidcs	NFSERR_ROFS,
394284741Sdavidcs	NFSERR_NAMETOL,
395284741Sdavidcs	NFSERR_STALE,
396284741Sdavidcs	NFSERR_BADHANDLE,
397284741Sdavidcs	NFSERR_SERVERFAULT,
398284741Sdavidcs	0,
399284741Sdavidcs};
400284741Sdavidcs
401284741Sdavidcsstatic short nfsv3err_rmdir[] = {
402284741Sdavidcs	NFSERR_IO,
403284741Sdavidcs	NFSERR_NOENT,
404284741Sdavidcs	NFSERR_IO,
405284741Sdavidcs	NFSERR_ACCES,
406284741Sdavidcs	NFSERR_EXIST,
407284741Sdavidcs	NFSERR_NOTDIR,
408284741Sdavidcs	NFSERR_INVAL,
409284741Sdavidcs	NFSERR_ROFS,
410284741Sdavidcs	NFSERR_NAMETOL,
411284741Sdavidcs	NFSERR_NOTEMPTY,
412284741Sdavidcs	NFSERR_STALE,
413284741Sdavidcs	NFSERR_BADHANDLE,
414284741Sdavidcs	NFSERR_NOTSUPP,
415284741Sdavidcs	NFSERR_SERVERFAULT,
416284741Sdavidcs	0,
417284741Sdavidcs};
418284741Sdavidcs
419284741Sdavidcsstatic short nfsv3err_rename[] = {
420284741Sdavidcs	NFSERR_IO,
421284741Sdavidcs	NFSERR_NOENT,
422284741Sdavidcs	NFSERR_IO,
423284741Sdavidcs	NFSERR_ACCES,
424284741Sdavidcs	NFSERR_EXIST,
425284741Sdavidcs	NFSERR_XDEV,
426284741Sdavidcs	NFSERR_NOTDIR,
427284741Sdavidcs	NFSERR_ISDIR,
428284741Sdavidcs	NFSERR_INVAL,
429284741Sdavidcs	NFSERR_NOSPC,
430284741Sdavidcs	NFSERR_ROFS,
431284741Sdavidcs	NFSERR_MLINK,
432284741Sdavidcs	NFSERR_NAMETOL,
433284741Sdavidcs	NFSERR_NOTEMPTY,
434284741Sdavidcs	NFSERR_DQUOT,
435284741Sdavidcs	NFSERR_STALE,
436284741Sdavidcs	NFSERR_BADHANDLE,
437284741Sdavidcs	NFSERR_NOTSUPP,
438284741Sdavidcs	NFSERR_SERVERFAULT,
439284741Sdavidcs	0,
440284741Sdavidcs};
441284741Sdavidcs
442284741Sdavidcsstatic short nfsv3err_link[] = {
443284741Sdavidcs	NFSERR_IO,
444284741Sdavidcs	NFSERR_IO,
445284741Sdavidcs	NFSERR_ACCES,
446284741Sdavidcs	NFSERR_EXIST,
447284741Sdavidcs	NFSERR_XDEV,
448284741Sdavidcs	NFSERR_NOTDIR,
449284741Sdavidcs	NFSERR_INVAL,
450284741Sdavidcs	NFSERR_NOSPC,
451284741Sdavidcs	NFSERR_ROFS,
452284741Sdavidcs	NFSERR_MLINK,
453284741Sdavidcs	NFSERR_NAMETOL,
454284741Sdavidcs	NFSERR_DQUOT,
455284741Sdavidcs	NFSERR_STALE,
456284741Sdavidcs	NFSERR_BADHANDLE,
457284741Sdavidcs	NFSERR_NOTSUPP,
458284741Sdavidcs	NFSERR_SERVERFAULT,
459284741Sdavidcs	0,
460284741Sdavidcs};
461284741Sdavidcs
462284741Sdavidcsstatic short nfsv3err_readdir[] = {
463284741Sdavidcs	NFSERR_IO,
464284741Sdavidcs	NFSERR_IO,
465284741Sdavidcs	NFSERR_ACCES,
466284741Sdavidcs	NFSERR_NOTDIR,
467284741Sdavidcs	NFSERR_STALE,
468284741Sdavidcs	NFSERR_BADHANDLE,
469284741Sdavidcs	NFSERR_BAD_COOKIE,
470284741Sdavidcs	NFSERR_TOOSMALL,
471284741Sdavidcs	NFSERR_SERVERFAULT,
472284741Sdavidcs	0,
473284741Sdavidcs};
474284741Sdavidcs
475284741Sdavidcsstatic short nfsv3err_readdirplus[] = {
476284741Sdavidcs	NFSERR_IO,
477284741Sdavidcs	NFSERR_IO,
478284741Sdavidcs	NFSERR_ACCES,
479284741Sdavidcs	NFSERR_NOTDIR,
480284741Sdavidcs	NFSERR_STALE,
481284741Sdavidcs	NFSERR_BADHANDLE,
482284741Sdavidcs	NFSERR_BAD_COOKIE,
483284741Sdavidcs	NFSERR_NOTSUPP,
484284741Sdavidcs	NFSERR_TOOSMALL,
485284741Sdavidcs	NFSERR_SERVERFAULT,
486284741Sdavidcs	0,
487284741Sdavidcs};
488284741Sdavidcs
489284741Sdavidcsstatic short nfsv3err_fsstat[] = {
490284741Sdavidcs	NFSERR_IO,
491284741Sdavidcs	NFSERR_IO,
492284741Sdavidcs	NFSERR_STALE,
493284741Sdavidcs	NFSERR_BADHANDLE,
494284741Sdavidcs	NFSERR_SERVERFAULT,
495284741Sdavidcs	0,
496284741Sdavidcs};
497284741Sdavidcs
498284741Sdavidcsstatic short nfsv3err_fsinfo[] = {
499284741Sdavidcs	NFSERR_STALE,
500284741Sdavidcs	NFSERR_STALE,
501284741Sdavidcs	NFSERR_BADHANDLE,
502284741Sdavidcs	NFSERR_SERVERFAULT,
503284741Sdavidcs	0,
504284741Sdavidcs};
505284741Sdavidcs
506284741Sdavidcsstatic short nfsv3err_pathconf[] = {
507284741Sdavidcs	NFSERR_STALE,
508284741Sdavidcs	NFSERR_STALE,
509284741Sdavidcs	NFSERR_BADHANDLE,
510284741Sdavidcs	NFSERR_SERVERFAULT,
511284741Sdavidcs	0,
512284741Sdavidcs};
513284741Sdavidcs
514284741Sdavidcsstatic short nfsv3err_commit[] = {
515284741Sdavidcs	NFSERR_IO,
516284741Sdavidcs	NFSERR_IO,
517284741Sdavidcs	NFSERR_STALE,
518284741Sdavidcs	NFSERR_BADHANDLE,
519284741Sdavidcs	NFSERR_SERVERFAULT,
520284741Sdavidcs	0,
521284741Sdavidcs};
522284741Sdavidcs
523284741Sdavidcsstatic short *nfsrv_v3errmap[] = {
524284741Sdavidcs	nfsv3err_null,
525284741Sdavidcs	nfsv3err_getattr,
526284741Sdavidcs	nfsv3err_setattr,
527284741Sdavidcs	nfsv3err_lookup,
528284741Sdavidcs	nfsv3err_access,
529284741Sdavidcs	nfsv3err_readlink,
530284741Sdavidcs	nfsv3err_read,
531284741Sdavidcs	nfsv3err_write,
532284741Sdavidcs	nfsv3err_create,
533284741Sdavidcs	nfsv3err_mkdir,
534284741Sdavidcs	nfsv3err_symlink,
535284741Sdavidcs	nfsv3err_mknod,
536284741Sdavidcs	nfsv3err_remove,
537284741Sdavidcs	nfsv3err_rmdir,
538284741Sdavidcs	nfsv3err_rename,
539284741Sdavidcs	nfsv3err_link,
540284741Sdavidcs	nfsv3err_readdir,
541284741Sdavidcs	nfsv3err_readdirplus,
542284741Sdavidcs	nfsv3err_fsstat,
543284741Sdavidcs	nfsv3err_fsinfo,
544284741Sdavidcs	nfsv3err_pathconf,
545284741Sdavidcs	nfsv3err_commit,
546284741Sdavidcs};
547284741Sdavidcs
548284741Sdavidcs#endif /* NFS_NOSERVER */
549284741Sdavidcs
550284741Sdavidcsextern struct nfsrtt nfsrtt;
551284741Sdavidcsextern time_t nqnfsstarttime;
552284741Sdavidcsextern int nqsrv_clockskew;
553284741Sdavidcsextern int nqsrv_writeslack;
554284741Sdavidcsextern int nqsrv_maxlease;
555284741Sdavidcsextern struct nfsstats nfsstats;
556284741Sdavidcsextern int nqnfs_piggy[NFS_NPROCS];
557284741Sdavidcsextern nfstype nfsv2_type[9];
558284741Sdavidcsextern nfstype nfsv3_type[9];
559284741Sdavidcsextern struct nfsnodehashhead *nfsnodehashtbl;
560284741Sdavidcsextern u_long nfsnodehash;
561284741Sdavidcs
562284741Sdavidcsstruct getfh_args;
563284741Sdavidcsextern int getfh(struct proc *, struct getfh_args *, int *);
564284741Sdavidcsstruct nfssvc_args;
565284741Sdavidcsextern int nfssvc(struct proc *, struct nfssvc_args *, int *);
566284741Sdavidcs
567284741SdavidcsLIST_HEAD(nfsnodehashhead, nfsnode);
568284741Sdavidcs
569284741Sdavidcsint nfs_webnamei __P((struct nameidata *, struct vnode *, struct proc *));
570284741Sdavidcs
571284741Sdavidcsu_quad_t
572284741Sdavidcsnfs_curusec()
573284741Sdavidcs{
574284741Sdavidcs	struct timeval tv;
575284741Sdavidcs
576284741Sdavidcs	getmicrotime(&tv);
577284741Sdavidcs	return ((u_quad_t)tv.tv_sec * 1000000 + (u_quad_t)tv.tv_usec);
578284741Sdavidcs}
579284741Sdavidcs
580284741Sdavidcs/*
581284741Sdavidcs * Create the header for an rpc request packet
582284741Sdavidcs * The hsiz is the size of the rest of the nfs request header.
583284741Sdavidcs * (just used to decide if a cluster is a good idea)
584284741Sdavidcs */
585284741Sdavidcsstruct mbuf *
586284741Sdavidcsnfsm_reqh(vp, procid, hsiz, bposp)
587284741Sdavidcs	struct vnode *vp;
588284741Sdavidcs	u_long procid;
589284741Sdavidcs	int hsiz;
590284741Sdavidcs	caddr_t *bposp;
591284741Sdavidcs{
592284741Sdavidcs	register struct mbuf *mb;
593284741Sdavidcs	register u_int32_t *tl;
594284741Sdavidcs	register caddr_t bpos;
595284741Sdavidcs	struct mbuf *mb2;
596284741Sdavidcs	struct nfsmount *nmp;
597284741Sdavidcs	int nqflag;
598284741Sdavidcs
599284741Sdavidcs	MGET(mb, M_WAIT, MT_DATA);
600284741Sdavidcs	if (hsiz >= MINCLSIZE)
601284741Sdavidcs		MCLGET(mb, M_WAIT);
602284741Sdavidcs	mb->m_len = 0;
603284741Sdavidcs	bpos = mtod(mb, caddr_t);
604284741Sdavidcs
605284741Sdavidcs	/*
606284741Sdavidcs	 * For NQNFS, add lease request.
607284741Sdavidcs	 */
608284741Sdavidcs	if (vp) {
609284741Sdavidcs		nmp = VFSTONFS(vp->v_mount);
610284741Sdavidcs		if (nmp->nm_flag & NFSMNT_NQNFS) {
611284741Sdavidcs			nqflag = NQNFS_NEEDLEASE(vp, procid);
612284741Sdavidcs			if (nqflag) {
613284741Sdavidcs				nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
614284741Sdavidcs				*tl++ = txdr_unsigned(nqflag);
615284741Sdavidcs				*tl = txdr_unsigned(nmp->nm_leaseterm);
616284741Sdavidcs			} else {
617284741Sdavidcs				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
618284741Sdavidcs				*tl = 0;
619284741Sdavidcs			}
620284741Sdavidcs		}
621284741Sdavidcs	}
622284741Sdavidcs	/* Finally, return values */
623284741Sdavidcs	*bposp = bpos;
624284741Sdavidcs	return (mb);
625284741Sdavidcs}
626284741Sdavidcs
627284741Sdavidcs/*
628284741Sdavidcs * Build the RPC header and fill in the authorization info.
629284741Sdavidcs * The authorization string argument is only used when the credentials
630284741Sdavidcs * come from outside of the kernel.
631284741Sdavidcs * Returns the head of the mbuf list.
632284741Sdavidcs */
633284741Sdavidcsstruct mbuf *
634284741Sdavidcsnfsm_rpchead(cr, nmflag, procid, auth_type, auth_len, auth_str, verf_len,
635284741Sdavidcs	verf_str, mrest, mrest_len, mbp, xidp)
636284741Sdavidcs	register struct ucred *cr;
637284741Sdavidcs	int nmflag;
638284741Sdavidcs	int procid;
639284741Sdavidcs	int auth_type;
640284741Sdavidcs	int auth_len;
641284741Sdavidcs	char *auth_str;
642284741Sdavidcs	int verf_len;
643284741Sdavidcs	char *verf_str;
644284741Sdavidcs	struct mbuf *mrest;
645284741Sdavidcs	int mrest_len;
646284741Sdavidcs	struct mbuf **mbp;
647284741Sdavidcs	u_int32_t *xidp;
648284741Sdavidcs{
649284741Sdavidcs	register struct mbuf *mb;
650284741Sdavidcs	register u_int32_t *tl;
651284741Sdavidcs	register caddr_t bpos;
652284741Sdavidcs	register int i;
653284741Sdavidcs	struct mbuf *mreq, *mb2;
654284741Sdavidcs	int siz, grpsiz, authsiz;
655284741Sdavidcs
656284741Sdavidcs	authsiz = nfsm_rndup(auth_len);
657284741Sdavidcs	MGETHDR(mb, M_WAIT, MT_DATA);
658284741Sdavidcs	if ((authsiz + 10 * NFSX_UNSIGNED) >= MINCLSIZE) {
659284741Sdavidcs		MCLGET(mb, M_WAIT);
660284741Sdavidcs	} else if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) {
661284741Sdavidcs		MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED);
662284741Sdavidcs	} else {
663284741Sdavidcs		MH_ALIGN(mb, 8 * NFSX_UNSIGNED);
664284741Sdavidcs	}
665284741Sdavidcs	mb->m_len = 0;
666284741Sdavidcs	mreq = mb;
667284741Sdavidcs	bpos = mtod(mb, caddr_t);
668284741Sdavidcs
669284741Sdavidcs	/*
670284741Sdavidcs	 * First the RPC header.
671284741Sdavidcs	 */
672284741Sdavidcs	nfsm_build(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
673284741Sdavidcs
674284741Sdavidcs	/* Get a pretty random xid to start with */
675284741Sdavidcs	if (!nfs_xid)
676284741Sdavidcs		nfs_xid = random();
677284741Sdavidcs	/*
678284741Sdavidcs	 * Skip zero xid if it should ever happen.
679284741Sdavidcs	 */
680284741Sdavidcs	if (++nfs_xid == 0)
681284741Sdavidcs		nfs_xid++;
682284741Sdavidcs
683284741Sdavidcs	*tl++ = *xidp = txdr_unsigned(nfs_xid);
684284741Sdavidcs	*tl++ = rpc_call;
685284741Sdavidcs	*tl++ = rpc_vers;
686284741Sdavidcs	if (nmflag & NFSMNT_NQNFS) {
687284741Sdavidcs		*tl++ = txdr_unsigned(NQNFS_PROG);
688284741Sdavidcs		*tl++ = txdr_unsigned(NQNFS_VER3);
689284741Sdavidcs	} else {
690284741Sdavidcs		*tl++ = txdr_unsigned(NFS_PROG);
691284741Sdavidcs		if (nmflag & NFSMNT_NFSV3)
692284741Sdavidcs			*tl++ = txdr_unsigned(NFS_VER3);
693284741Sdavidcs		else
694284741Sdavidcs			*tl++ = txdr_unsigned(NFS_VER2);
695284741Sdavidcs	}
696284741Sdavidcs	if (nmflag & NFSMNT_NFSV3)
697284741Sdavidcs		*tl++ = txdr_unsigned(procid);
698284741Sdavidcs	else
699284741Sdavidcs		*tl++ = txdr_unsigned(nfsv2_procid[procid]);
700284741Sdavidcs
701284741Sdavidcs	/*
702284741Sdavidcs	 * And then the authorization cred.
703284741Sdavidcs	 */
704284741Sdavidcs	*tl++ = txdr_unsigned(auth_type);
705284741Sdavidcs	*tl = txdr_unsigned(authsiz);
706284741Sdavidcs	switch (auth_type) {
707284741Sdavidcs	case RPCAUTH_UNIX:
708284741Sdavidcs		nfsm_build(tl, u_int32_t *, auth_len);
709284741Sdavidcs		*tl++ = 0;		/* stamp ?? */
710284741Sdavidcs		*tl++ = 0;		/* NULL hostname */
711284741Sdavidcs		*tl++ = txdr_unsigned(cr->cr_uid);
712284741Sdavidcs		*tl++ = txdr_unsigned(cr->cr_groups[0]);
713284741Sdavidcs		grpsiz = (auth_len >> 2) - 5;
714284741Sdavidcs		*tl++ = txdr_unsigned(grpsiz);
715284741Sdavidcs		for (i = 1; i <= grpsiz; i++)
716284741Sdavidcs			*tl++ = txdr_unsigned(cr->cr_groups[i]);
717284741Sdavidcs		break;
718284741Sdavidcs	case RPCAUTH_KERB4:
719284741Sdavidcs		siz = auth_len;
720284741Sdavidcs		while (siz > 0) {
721284741Sdavidcs			if (M_TRAILINGSPACE(mb) == 0) {
722284741Sdavidcs				MGET(mb2, M_WAIT, MT_DATA);
723284741Sdavidcs				if (siz >= MINCLSIZE)
724284741Sdavidcs					MCLGET(mb2, M_WAIT);
725284741Sdavidcs				mb->m_next = mb2;
726284741Sdavidcs				mb = mb2;
727284741Sdavidcs				mb->m_len = 0;
728284741Sdavidcs				bpos = mtod(mb, caddr_t);
729284741Sdavidcs			}
730284741Sdavidcs			i = min(siz, M_TRAILINGSPACE(mb));
731284741Sdavidcs			bcopy(auth_str, bpos, i);
732284741Sdavidcs			mb->m_len += i;
733284741Sdavidcs			auth_str += i;
734284741Sdavidcs			bpos += i;
735284741Sdavidcs			siz -= i;
736284741Sdavidcs		}
737284741Sdavidcs		if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
738284741Sdavidcs			for (i = 0; i < siz; i++)
739284741Sdavidcs				*bpos++ = '\0';
740284741Sdavidcs			mb->m_len += siz;
741284741Sdavidcs		}
742284741Sdavidcs		break;
743284741Sdavidcs	};
744284741Sdavidcs
745284741Sdavidcs	/*
746284741Sdavidcs	 * And the verifier...
747284741Sdavidcs	 */
748284741Sdavidcs	nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
749284741Sdavidcs	if (verf_str) {
750284741Sdavidcs		*tl++ = txdr_unsigned(RPCAUTH_KERB4);
751284741Sdavidcs		*tl = txdr_unsigned(verf_len);
752284741Sdavidcs		siz = verf_len;
753284741Sdavidcs		while (siz > 0) {
754284741Sdavidcs			if (M_TRAILINGSPACE(mb) == 0) {
755284741Sdavidcs				MGET(mb2, M_WAIT, MT_DATA);
756284741Sdavidcs				if (siz >= MINCLSIZE)
757284741Sdavidcs					MCLGET(mb2, M_WAIT);
758284741Sdavidcs				mb->m_next = mb2;
759284741Sdavidcs				mb = mb2;
760284741Sdavidcs				mb->m_len = 0;
761284741Sdavidcs				bpos = mtod(mb, caddr_t);
762284741Sdavidcs			}
763284741Sdavidcs			i = min(siz, M_TRAILINGSPACE(mb));
764284741Sdavidcs			bcopy(verf_str, bpos, i);
765284741Sdavidcs			mb->m_len += i;
766284741Sdavidcs			verf_str += i;
767284741Sdavidcs			bpos += i;
768284741Sdavidcs			siz -= i;
769284741Sdavidcs		}
770284741Sdavidcs		if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) {
771284741Sdavidcs			for (i = 0; i < siz; i++)
772284741Sdavidcs				*bpos++ = '\0';
773284741Sdavidcs			mb->m_len += siz;
774284741Sdavidcs		}
775284741Sdavidcs	} else {
776284741Sdavidcs		*tl++ = txdr_unsigned(RPCAUTH_NULL);
777284741Sdavidcs		*tl = 0;
778284741Sdavidcs	}
779284741Sdavidcs	mb->m_next = mrest;
780284741Sdavidcs	mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
781284741Sdavidcs	mreq->m_pkthdr.rcvif = (struct ifnet *)0;
782284741Sdavidcs	*mbp = mb;
783284741Sdavidcs	return (mreq);
784284741Sdavidcs}
785284741Sdavidcs
786284741Sdavidcs/*
787284741Sdavidcs * copies mbuf chain to the uio scatter/gather list
788284741Sdavidcs */
789284741Sdavidcsint
790284741Sdavidcsnfsm_mbuftouio(mrep, uiop, siz, dpos)
791284741Sdavidcs	struct mbuf **mrep;
792284741Sdavidcs	register struct uio *uiop;
793284741Sdavidcs	int siz;
794284741Sdavidcs	caddr_t *dpos;
795284741Sdavidcs{
796284741Sdavidcs	register char *mbufcp, *uiocp;
797284741Sdavidcs	register int xfer, left, len;
798284741Sdavidcs	register struct mbuf *mp;
799284741Sdavidcs	long uiosiz, rem;
800284741Sdavidcs	int error = 0;
801284741Sdavidcs
802284741Sdavidcs	mp = *mrep;
803284741Sdavidcs	mbufcp = *dpos;
804284741Sdavidcs	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
805284741Sdavidcs	rem = nfsm_rndup(siz)-siz;
806284741Sdavidcs	while (siz > 0) {
807284741Sdavidcs		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
808284741Sdavidcs			return (EFBIG);
809284741Sdavidcs		left = uiop->uio_iov->iov_len;
810284741Sdavidcs		uiocp = uiop->uio_iov->iov_base;
811284741Sdavidcs		if (left > siz)
812284741Sdavidcs			left = siz;
813284741Sdavidcs		uiosiz = left;
814284741Sdavidcs		while (left > 0) {
815284741Sdavidcs			while (len == 0) {
816284741Sdavidcs				mp = mp->m_next;
817284741Sdavidcs				if (mp == NULL)
818284741Sdavidcs					return (EBADRPC);
819284741Sdavidcs				mbufcp = mtod(mp, caddr_t);
820284741Sdavidcs				len = mp->m_len;
821284741Sdavidcs			}
822284741Sdavidcs			xfer = (left > len) ? len : left;
823284741Sdavidcs#ifdef notdef
824284741Sdavidcs			/* Not Yet.. */
825284741Sdavidcs			if (uiop->uio_iov->iov_op != NULL)
826284741Sdavidcs				(*(uiop->uio_iov->iov_op))
827284741Sdavidcs				(mbufcp, uiocp, xfer);
828284741Sdavidcs			else
829284741Sdavidcs#endif
830284741Sdavidcs			if (uiop->uio_segflg == UIO_SYSSPACE)
831284741Sdavidcs				bcopy(mbufcp, uiocp, xfer);
832284741Sdavidcs			else
833284741Sdavidcs				copyout(mbufcp, uiocp, xfer);
834284741Sdavidcs			left -= xfer;
835284741Sdavidcs			len -= xfer;
836284741Sdavidcs			mbufcp += xfer;
837284741Sdavidcs			uiocp += xfer;
838284741Sdavidcs			uiop->uio_offset += xfer;
839284741Sdavidcs			uiop->uio_resid -= xfer;
840284741Sdavidcs		}
841284741Sdavidcs		if (uiop->uio_iov->iov_len <= siz) {
842284741Sdavidcs			uiop->uio_iovcnt--;
843284741Sdavidcs			uiop->uio_iov++;
844284741Sdavidcs		} else {
845284741Sdavidcs			uiop->uio_iov->iov_base += uiosiz;
846284741Sdavidcs			uiop->uio_iov->iov_len -= uiosiz;
847284741Sdavidcs		}
848284741Sdavidcs		siz -= uiosiz;
849284741Sdavidcs	}
850284741Sdavidcs	*dpos = mbufcp;
851284741Sdavidcs	*mrep = mp;
852284741Sdavidcs	if (rem > 0) {
853284741Sdavidcs		if (len < rem)
854284741Sdavidcs			error = nfs_adv(mrep, dpos, rem, len);
855284741Sdavidcs		else
856284741Sdavidcs			*dpos += rem;
857284741Sdavidcs	}
858284741Sdavidcs	return (error);
859284741Sdavidcs}
860284741Sdavidcs
861284741Sdavidcs/*
862284741Sdavidcs * copies a uio scatter/gather list to an mbuf chain.
863284741Sdavidcs * NOTE: can ony handle iovcnt == 1
864284741Sdavidcs */
865284741Sdavidcsint
866284741Sdavidcsnfsm_uiotombuf(uiop, mq, siz, bpos)
867284741Sdavidcs	register struct uio *uiop;
868284741Sdavidcs	struct mbuf **mq;
869284741Sdavidcs	int siz;
870284741Sdavidcs	caddr_t *bpos;
871284741Sdavidcs{
872284741Sdavidcs	register char *uiocp;
873284741Sdavidcs	register struct mbuf *mp, *mp2;
874284741Sdavidcs	register int xfer, left, mlen;
875284741Sdavidcs	int uiosiz, clflg, rem;
876284741Sdavidcs	char *cp;
877284741Sdavidcs
878284741Sdavidcs#ifdef DIAGNOSTIC
879284741Sdavidcs	if (uiop->uio_iovcnt != 1)
880284741Sdavidcs		panic("nfsm_uiotombuf: iovcnt != 1");
881284741Sdavidcs#endif
882284741Sdavidcs
883284741Sdavidcs	if (siz > MLEN)		/* or should it >= MCLBYTES ?? */
884284741Sdavidcs		clflg = 1;
885284741Sdavidcs	else
886284741Sdavidcs		clflg = 0;
887284741Sdavidcs	rem = nfsm_rndup(siz)-siz;
888284741Sdavidcs	mp = mp2 = *mq;
889284741Sdavidcs	while (siz > 0) {
890284741Sdavidcs		left = uiop->uio_iov->iov_len;
891284741Sdavidcs		uiocp = uiop->uio_iov->iov_base;
892284741Sdavidcs		if (left > siz)
893284741Sdavidcs			left = siz;
894284741Sdavidcs		uiosiz = left;
895284741Sdavidcs		while (left > 0) {
896284741Sdavidcs			mlen = M_TRAILINGSPACE(mp);
897284741Sdavidcs			if (mlen == 0) {
898284741Sdavidcs				MGET(mp, M_WAIT, MT_DATA);
899284741Sdavidcs				if (clflg)
900284741Sdavidcs					MCLGET(mp, M_WAIT);
901284741Sdavidcs				mp->m_len = 0;
902284741Sdavidcs				mp2->m_next = mp;
903284741Sdavidcs				mp2 = mp;
904284741Sdavidcs				mlen = M_TRAILINGSPACE(mp);
905284741Sdavidcs			}
906284741Sdavidcs			xfer = (left > mlen) ? mlen : left;
907284741Sdavidcs#ifdef notdef
908284741Sdavidcs			/* Not Yet.. */
909284741Sdavidcs			if (uiop->uio_iov->iov_op != NULL)
910284741Sdavidcs				(*(uiop->uio_iov->iov_op))
911284741Sdavidcs				(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
912284741Sdavidcs			else
913284741Sdavidcs#endif
914284741Sdavidcs			if (uiop->uio_segflg == UIO_SYSSPACE)
915284741Sdavidcs				bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
916284741Sdavidcs			else
917284741Sdavidcs				copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
918284741Sdavidcs			mp->m_len += xfer;
919284741Sdavidcs			left -= xfer;
920284741Sdavidcs			uiocp += xfer;
921284741Sdavidcs			uiop->uio_offset += xfer;
922284741Sdavidcs			uiop->uio_resid -= xfer;
923284741Sdavidcs		}
924284741Sdavidcs		uiop->uio_iov->iov_base += uiosiz;
925284741Sdavidcs		uiop->uio_iov->iov_len -= uiosiz;
926284741Sdavidcs		siz -= uiosiz;
927284741Sdavidcs	}
928284741Sdavidcs	if (rem > 0) {
929284741Sdavidcs		if (rem > M_TRAILINGSPACE(mp)) {
930284741Sdavidcs			MGET(mp, M_WAIT, MT_DATA);
931284741Sdavidcs			mp->m_len = 0;
932284741Sdavidcs			mp2->m_next = mp;
933284741Sdavidcs		}
934284741Sdavidcs		cp = mtod(mp, caddr_t)+mp->m_len;
935284741Sdavidcs		for (left = 0; left < rem; left++)
936284741Sdavidcs			*cp++ = '\0';
937284741Sdavidcs		mp->m_len += rem;
938284741Sdavidcs		*bpos = cp;
939284741Sdavidcs	} else
940284741Sdavidcs		*bpos = mtod(mp, caddr_t)+mp->m_len;
941284741Sdavidcs	*mq = mp;
942284741Sdavidcs	return (0);
943284741Sdavidcs}
944284741Sdavidcs
945284741Sdavidcs/*
946284741Sdavidcs * Help break down an mbuf chain by setting the first siz bytes contiguous
947284741Sdavidcs * pointed to by returned val.
948284741Sdavidcs * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
949284741Sdavidcs * cases. (The macros use the vars. dpos and dpos2)
950284741Sdavidcs */
951284741Sdavidcsint
952284741Sdavidcsnfsm_disct(mdp, dposp, siz, left, cp2)
953284741Sdavidcs	struct mbuf **mdp;
954284741Sdavidcs	caddr_t *dposp;
955284741Sdavidcs	int siz;
956284741Sdavidcs	int left;
957284741Sdavidcs	caddr_t *cp2;
958284741Sdavidcs{
959284741Sdavidcs	register struct mbuf *mp, *mp2;
960284741Sdavidcs	register int siz2, xfer;
961284741Sdavidcs	register caddr_t p;
962284741Sdavidcs
963284741Sdavidcs	mp = *mdp;
964284741Sdavidcs	while (left == 0) {
965284741Sdavidcs		*mdp = mp = mp->m_next;
966284741Sdavidcs		if (mp == NULL)
967284741Sdavidcs			return (EBADRPC);
968284741Sdavidcs		left = mp->m_len;
969284741Sdavidcs		*dposp = mtod(mp, caddr_t);
970284741Sdavidcs	}
971284741Sdavidcs	if (left >= siz) {
972284741Sdavidcs		*cp2 = *dposp;
973284741Sdavidcs		*dposp += siz;
974284741Sdavidcs	} else if (mp->m_next == NULL) {
975284741Sdavidcs		return (EBADRPC);
976284741Sdavidcs	} else if (siz > MHLEN) {
977284741Sdavidcs		panic("nfs S too big");
978284741Sdavidcs	} else {
979284741Sdavidcs		MGET(mp2, M_WAIT, MT_DATA);
980284741Sdavidcs		mp2->m_next = mp->m_next;
981284741Sdavidcs		mp->m_next = mp2;
982284741Sdavidcs		mp->m_len -= left;
983284741Sdavidcs		mp = mp2;
984284741Sdavidcs		*cp2 = p = mtod(mp, caddr_t);
985284741Sdavidcs		bcopy(*dposp, p, left);		/* Copy what was left */
986284741Sdavidcs		siz2 = siz-left;
987284741Sdavidcs		p += left;
988284741Sdavidcs		mp2 = mp->m_next;
989284741Sdavidcs		/* Loop around copying up the siz2 bytes */
990284741Sdavidcs		while (siz2 > 0) {
991284741Sdavidcs			if (mp2 == NULL)
992284741Sdavidcs				return (EBADRPC);
993284741Sdavidcs			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
994284741Sdavidcs			if (xfer > 0) {
995284741Sdavidcs				bcopy(mtod(mp2, caddr_t), p, xfer);
996284741Sdavidcs				NFSMADV(mp2, xfer);
997284741Sdavidcs				mp2->m_len -= xfer;
998284741Sdavidcs				p += xfer;
999284741Sdavidcs				siz2 -= xfer;
1000284741Sdavidcs			}
1001284741Sdavidcs			if (siz2 > 0)
1002284741Sdavidcs				mp2 = mp2->m_next;
1003284741Sdavidcs		}
1004284741Sdavidcs		mp->m_len = siz;
1005284741Sdavidcs		*mdp = mp2;
1006284741Sdavidcs		*dposp = mtod(mp2, caddr_t);
1007284741Sdavidcs	}
1008284741Sdavidcs	return (0);
1009284741Sdavidcs}
1010284741Sdavidcs
1011284741Sdavidcs/*
1012284741Sdavidcs * Advance the position in the mbuf chain.
1013284741Sdavidcs */
1014284741Sdavidcsint
1015284741Sdavidcsnfs_adv(mdp, dposp, offs, left)
1016284741Sdavidcs	struct mbuf **mdp;
1017284741Sdavidcs	caddr_t *dposp;
1018284741Sdavidcs	int offs;
1019284741Sdavidcs	int left;
1020284741Sdavidcs{
1021284741Sdavidcs	register struct mbuf *m;
1022284741Sdavidcs	register int s;
1023284741Sdavidcs
1024284741Sdavidcs	m = *mdp;
1025284741Sdavidcs	s = left;
1026284741Sdavidcs	while (s < offs) {
1027284741Sdavidcs		offs -= s;
1028284741Sdavidcs		m = m->m_next;
1029284741Sdavidcs		if (m == NULL)
1030284741Sdavidcs			return (EBADRPC);
1031284741Sdavidcs		s = m->m_len;
1032284741Sdavidcs	}
1033284741Sdavidcs	*mdp = m;
1034284741Sdavidcs	*dposp = mtod(m, caddr_t)+offs;
1035284741Sdavidcs	return (0);
1036284741Sdavidcs}
1037284741Sdavidcs
1038284741Sdavidcs/*
1039284741Sdavidcs * Copy a string into mbufs for the hard cases...
1040284741Sdavidcs */
1041284741Sdavidcsint
1042284741Sdavidcsnfsm_strtmbuf(mb, bpos, cp, siz)
1043284741Sdavidcs	struct mbuf **mb;
1044284741Sdavidcs	char **bpos;
1045284741Sdavidcs	const char *cp;
1046284741Sdavidcs	long siz;
1047284741Sdavidcs{
1048284741Sdavidcs	register struct mbuf *m1 = NULL, *m2;
1049284741Sdavidcs	long left, xfer, len, tlen;
1050284741Sdavidcs	u_int32_t *tl;
1051284741Sdavidcs	int putsize;
1052284741Sdavidcs
1053284741Sdavidcs	putsize = 1;
1054284741Sdavidcs	m2 = *mb;
1055284741Sdavidcs	left = M_TRAILINGSPACE(m2);
1056284741Sdavidcs	if (left > 0) {
1057284741Sdavidcs		tl = ((u_int32_t *)(*bpos));
1058284741Sdavidcs		*tl++ = txdr_unsigned(siz);
1059284741Sdavidcs		putsize = 0;
1060284741Sdavidcs		left -= NFSX_UNSIGNED;
1061284741Sdavidcs		m2->m_len += NFSX_UNSIGNED;
1062284741Sdavidcs		if (left > 0) {
1063284741Sdavidcs			bcopy(cp, (caddr_t) tl, left);
1064284741Sdavidcs			siz -= left;
1065284741Sdavidcs			cp += left;
1066284741Sdavidcs			m2->m_len += left;
1067284741Sdavidcs			left = 0;
1068284741Sdavidcs		}
1069284741Sdavidcs	}
1070284741Sdavidcs	/* Loop around adding mbufs */
1071284741Sdavidcs	while (siz > 0) {
1072284741Sdavidcs		MGET(m1, M_WAIT, MT_DATA);
1073284741Sdavidcs		if (siz > MLEN)
1074284741Sdavidcs			MCLGET(m1, M_WAIT);
1075284741Sdavidcs		m1->m_len = NFSMSIZ(m1);
1076284741Sdavidcs		m2->m_next = m1;
1077284741Sdavidcs		m2 = m1;
1078284741Sdavidcs		tl = mtod(m1, u_int32_t *);
1079284741Sdavidcs		tlen = 0;
1080284741Sdavidcs		if (putsize) {
1081284741Sdavidcs			*tl++ = txdr_unsigned(siz);
1082284741Sdavidcs			m1->m_len -= NFSX_UNSIGNED;
1083284741Sdavidcs			tlen = NFSX_UNSIGNED;
1084284741Sdavidcs			putsize = 0;
1085284741Sdavidcs		}
1086284741Sdavidcs		if (siz < m1->m_len) {
1087284741Sdavidcs			len = nfsm_rndup(siz);
1088284741Sdavidcs			xfer = siz;
1089284741Sdavidcs			if (xfer < len)
1090284741Sdavidcs				*(tl+(xfer>>2)) = 0;
1091284741Sdavidcs		} else {
1092284741Sdavidcs			xfer = len = m1->m_len;
1093284741Sdavidcs		}
1094284741Sdavidcs		bcopy(cp, (caddr_t) tl, xfer);
1095284741Sdavidcs		m1->m_len = len+tlen;
1096284741Sdavidcs		siz -= xfer;
1097284741Sdavidcs		cp += xfer;
1098284741Sdavidcs	}
1099284741Sdavidcs	*mb = m1;
1100284741Sdavidcs	*bpos = mtod(m1, caddr_t)+m1->m_len;
1101284741Sdavidcs	return (0);
1102284741Sdavidcs}
1103284741Sdavidcs
1104284741Sdavidcs/*
1105284741Sdavidcs * Called once to initialize data structures...
1106284741Sdavidcs */
1107284741Sdavidcsint
1108284741Sdavidcsnfs_init(vfsp)
1109284741Sdavidcs	struct vfsconf *vfsp;
1110284741Sdavidcs{
1111284741Sdavidcs	register int i;
1112284741Sdavidcs
1113284741Sdavidcs	nfsmount_zone = zinit("NFSMOUNT", sizeof(struct nfsmount), 0, 0, 1);
1114284741Sdavidcs
1115284741Sdavidcs	/*
1116284741Sdavidcs	 * Check to see if major data structures haven't bloated.
1117284741Sdavidcs	 */
1118284741Sdavidcs	if (sizeof (struct nfssvc_sock) > NFS_SVCALLOC) {
1119284741Sdavidcs		printf("struct nfssvc_sock bloated (> %dbytes)\n",NFS_SVCALLOC);
1120284741Sdavidcs		printf("Try reducing NFS_UIDHASHSIZ\n");
1121284741Sdavidcs	}
1122284741Sdavidcs	if (sizeof (struct nfsuid) > NFS_UIDALLOC) {
1123284741Sdavidcs		printf("struct nfsuid bloated (> %dbytes)\n",NFS_UIDALLOC);
1124284741Sdavidcs		printf("Try unionizing the nu_nickname and nu_flag fields\n");
1125284741Sdavidcs	}
1126284741Sdavidcs	nfs_mount_type = vfsp->vfc_typenum;
1127284741Sdavidcs	nfsrtt.pos = 0;
1128284741Sdavidcs	rpc_vers = txdr_unsigned(RPC_VER2);
1129284741Sdavidcs	rpc_call = txdr_unsigned(RPC_CALL);
1130284741Sdavidcs	rpc_reply = txdr_unsigned(RPC_REPLY);
1131284741Sdavidcs	rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
1132284741Sdavidcs	rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
1133284741Sdavidcs	rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
1134284741Sdavidcs	rpc_autherr = txdr_unsigned(RPC_AUTHERR);
1135284741Sdavidcs	rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
1136284741Sdavidcs	rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4);
1137284741Sdavidcs	nfs_prog = txdr_unsigned(NFS_PROG);
1138284741Sdavidcs	nqnfs_prog = txdr_unsigned(NQNFS_PROG);
1139284741Sdavidcs	nfs_true = txdr_unsigned(TRUE);
1140284741Sdavidcs	nfs_false = txdr_unsigned(FALSE);
1141284741Sdavidcs	nfs_xdrneg1 = txdr_unsigned(-1);
1142284741Sdavidcs	nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
1143284741Sdavidcs	if (nfs_ticks < 1)
1144284741Sdavidcs		nfs_ticks = 1;
1145284741Sdavidcs	/* Ensure async daemons disabled */
1146284741Sdavidcs	for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
1147284741Sdavidcs		nfs_iodwant[i] = (struct proc *)0;
1148284741Sdavidcs		nfs_iodmount[i] = (struct nfsmount *)0;
1149284741Sdavidcs	}
1150284741Sdavidcs	nfs_nhinit();			/* Init the nfsnode table */
1151284741Sdavidcs#ifndef NFS_NOSERVER
1152284741Sdavidcs	nfsrv_init(0);			/* Init server data structures */
1153284741Sdavidcs	nfsrv_initcache();		/* Init the server request cache */
1154284741Sdavidcs#endif
1155284741Sdavidcs
1156284741Sdavidcs	/*
1157284741Sdavidcs	 * Initialize the nqnfs server stuff.
1158284741Sdavidcs	 */
1159284741Sdavidcs	if (nqnfsstarttime == 0) {
1160284741Sdavidcs		nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
1161284741Sdavidcs			+ nqsrv_clockskew + nqsrv_writeslack;
1162284741Sdavidcs		NQLOADNOVRAM(nqnfsstarttime);
1163284741Sdavidcs		CIRCLEQ_INIT(&nqtimerhead);
1164284741Sdavidcs		nqfhhashtbl = hashinit(NQLCHSZ, M_NQLEASE, &nqfhhash);
1165284741Sdavidcs	}
1166284741Sdavidcs
1167284741Sdavidcs	/*
1168284741Sdavidcs	 * Initialize reply list and start timer
1169284741Sdavidcs	 */
1170284741Sdavidcs	TAILQ_INIT(&nfs_reqq);
1171284741Sdavidcs
1172284741Sdavidcs	nfs_timer(0);
1173284741Sdavidcs
1174284741Sdavidcs	/*
1175284741Sdavidcs	 * Set up lease_check and lease_updatetime so that other parts
1176284741Sdavidcs	 * of the system can call us, if we are loadable.
1177284741Sdavidcs	 */
1178284741Sdavidcs#ifndef NFS_NOSERVER
1179284741Sdavidcs	nfs_prev_vop_lease_check = default_vnodeop_p[VOFFSET(vop_lease)];
1180284741Sdavidcs	default_vnodeop_p[VOFFSET(vop_lease)] = (vop_t *)nqnfs_vop_lease_check;
1181284741Sdavidcs#endif
1182284741Sdavidcs	nfs_prev_lease_updatetime = lease_updatetime;
1183284741Sdavidcs	lease_updatetime = nfs_lease_updatetime;
1184284741Sdavidcs	nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
1185284741Sdavidcs	sysent[SYS_nfssvc].sy_narg = 2;
1186284741Sdavidcs	nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
1187284741Sdavidcs	sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
1188284741Sdavidcs#ifndef NFS_NOSERVER
1189284741Sdavidcs	nfs_prev_getfh_sy_narg = sysent[SYS_getfh].sy_narg;
1190284741Sdavidcs	sysent[SYS_getfh].sy_narg = 2;
1191284741Sdavidcs	nfs_prev_getfh_sy_call = sysent[SYS_getfh].sy_call;
1192284741Sdavidcs	sysent[SYS_getfh].sy_call = (sy_call_t *)getfh;
1193284741Sdavidcs#endif
1194284741Sdavidcs
1195284741Sdavidcs	nfs_pbuf_freecnt = nswbuf / 2 + 1;
1196284741Sdavidcs
1197284741Sdavidcs	return (0);
1198284741Sdavidcs}
1199284741Sdavidcs
1200284741Sdavidcsint
1201284741Sdavidcsnfs_uninit(vfsp)
1202284741Sdavidcs	struct vfsconf *vfsp;
1203284741Sdavidcs{
1204284741Sdavidcs
1205284741Sdavidcs	untimeout(nfs_timer, (void *)NULL, nfs_timer_handle);
1206284741Sdavidcs	nfs_mount_type = -1;
1207284741Sdavidcs#ifndef NFS_NOSERVER
1208284741Sdavidcs	default_vnodeop_p[VOFFSET(vop_lease)] = nfs_prev_vop_lease_check;
1209284741Sdavidcs#endif
1210284741Sdavidcs	lease_updatetime = nfs_prev_lease_updatetime;
1211284741Sdavidcs	sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
1212284741Sdavidcs	sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
1213284741Sdavidcs#ifndef NFS_NOSERVER
1214284741Sdavidcs	sysent[SYS_getfh].sy_narg = nfs_prev_getfh_sy_narg;
1215284741Sdavidcs	sysent[SYS_getfh].sy_call = nfs_prev_getfh_sy_call;
1216284741Sdavidcs#endif
1217284741Sdavidcs	return (0);
1218284741Sdavidcs}
1219284741Sdavidcs
1220284741Sdavidcs/*
1221284741Sdavidcs * Attribute cache routines.
1222284741Sdavidcs * nfs_loadattrcache() - loads or updates the cache contents from attributes
1223284741Sdavidcs *	that are on the mbuf list
1224284741Sdavidcs * nfs_getattrcache() - returns valid attributes if found in cache, returns
1225284741Sdavidcs *	error otherwise
1226284741Sdavidcs */
1227284741Sdavidcs
1228284741Sdavidcs/*
1229284741Sdavidcs * Load the attribute cache (that lives in the nfsnode entry) with
1230284741Sdavidcs * the values on the mbuf list and
1231284741Sdavidcs * Iff vap not NULL
1232284741Sdavidcs *    copy the attributes to *vaper
1233284741Sdavidcs */
1234284741Sdavidcsint
1235284741Sdavidcsnfs_loadattrcache(vpp, mdp, dposp, vaper)
1236284741Sdavidcs	struct vnode **vpp;
1237284741Sdavidcs	struct mbuf **mdp;
1238284741Sdavidcs	caddr_t *dposp;
1239284741Sdavidcs	struct vattr *vaper;
1240284741Sdavidcs{
1241284741Sdavidcs	register struct vnode *vp = *vpp;
1242284741Sdavidcs	register struct vattr *vap;
1243284741Sdavidcs	register struct nfs_fattr *fp;
1244284741Sdavidcs	register struct nfsnode *np;
1245284741Sdavidcs	register int32_t t1;
1246284741Sdavidcs	caddr_t cp2;
1247284741Sdavidcs	int error = 0, rdev;
1248284741Sdavidcs	struct mbuf *md;
1249284741Sdavidcs	enum vtype vtyp;
1250284741Sdavidcs	u_short vmode;
1251284741Sdavidcs	struct timespec mtime;
1252284741Sdavidcs	struct vnode *nvp;
1253284741Sdavidcs	int v3 = NFS_ISV3(vp);
1254284741Sdavidcs
1255284741Sdavidcs	md = *mdp;
1256284741Sdavidcs	t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
1257284741Sdavidcs	if ((error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2)) != 0)
1258284741Sdavidcs		return (error);
1259284741Sdavidcs	fp = (struct nfs_fattr *)cp2;
1260284741Sdavidcs	if (v3) {
1261284741Sdavidcs		vtyp = nfsv3tov_type(fp->fa_type);
1262284741Sdavidcs		vmode = fxdr_unsigned(u_short, fp->fa_mode);
1263284741Sdavidcs		rdev = umakedev(fxdr_unsigned(int, fp->fa3_rdev.specdata1),
1264284741Sdavidcs			fxdr_unsigned(int, fp->fa3_rdev.specdata2));
1265284741Sdavidcs		fxdr_nfsv3time(&fp->fa3_mtime, &mtime);
1266284741Sdavidcs	} else {
1267284741Sdavidcs		vtyp = nfsv2tov_type(fp->fa_type);
1268284741Sdavidcs		vmode = fxdr_unsigned(u_short, fp->fa_mode);
1269284741Sdavidcs		/*
1270284741Sdavidcs		 * XXX
1271284741Sdavidcs		 *
1272284741Sdavidcs		 * The duplicate information returned in fa_type and fa_mode
1273284741Sdavidcs		 * is an ambiguity in the NFS version 2 protocol.
1274284741Sdavidcs		 *
1275284741Sdavidcs		 * VREG should be taken literally as a regular file.  If a
1276284741Sdavidcs		 * server intents to return some type information differently
1277284741Sdavidcs		 * in the upper bits of the mode field (e.g. for sockets, or
1278284741Sdavidcs		 * FIFOs), NFSv2 mandates fa_type to be VNON.  Anyway, we
1279284741Sdavidcs		 * leave the examination of the mode bits even in the VREG
1280284741Sdavidcs		 * case to avoid breakage for bogus servers, but we make sure
1281284741Sdavidcs		 * that there are actually type bits set in the upper part of
1282284741Sdavidcs		 * fa_mode (and failing that, trust the va_type field).
1283284741Sdavidcs		 *
1284284741Sdavidcs		 * NFSv3 cleared the issue, and requires fa_mode to not
1285284741Sdavidcs		 * contain any type information (while also introduing sockets
1286284741Sdavidcs		 * and FIFOs for fa_type).
1287284741Sdavidcs		 */
1288284741Sdavidcs		if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0))
1289284741Sdavidcs			vtyp = IFTOVT(vmode);
1290284741Sdavidcs		rdev = fxdr_unsigned(int32_t, fp->fa2_rdev);
1291284741Sdavidcs		fxdr_nfsv2time(&fp->fa2_mtime, &mtime);
1292284741Sdavidcs
1293284741Sdavidcs		/*
1294284741Sdavidcs		 * Really ugly NFSv2 kludge.
1295284741Sdavidcs		 */
1296284741Sdavidcs		if (vtyp == VCHR && rdev == 0xffffffff)
1297284741Sdavidcs			vtyp = VFIFO;
1298284741Sdavidcs	}
1299284741Sdavidcs
1300284741Sdavidcs	/*
1301284741Sdavidcs	 * If v_type == VNON it is a new node, so fill in the v_type,
1302284741Sdavidcs	 * n_mtime fields. Check to see if it represents a special
1303284741Sdavidcs	 * device, and if so, check for a possible alias. Once the
1304284741Sdavidcs	 * correct vnode has been obtained, fill in the rest of the
1305284741Sdavidcs	 * information.
1306284741Sdavidcs	 */
1307284741Sdavidcs	np = VTONFS(vp);
1308284741Sdavidcs	if (vp->v_type != vtyp) {
1309284741Sdavidcs		vp->v_type = vtyp;
1310284741Sdavidcs		if (vp->v_type == VFIFO) {
1311284741Sdavidcs			vp->v_op = fifo_nfsv2nodeop_p;
1312284741Sdavidcs		}
1313284741Sdavidcs		if (vp->v_type == VCHR || vp->v_type == VBLK) {
1314284741Sdavidcs			vp->v_op = spec_nfsv2nodeop_p;
1315284741Sdavidcs			nvp = checkalias(vp, rdev, vp->v_mount);
1316284741Sdavidcs			if (nvp) {
1317284741Sdavidcs				/*
1318284741Sdavidcs				 * Discard unneeded vnode, but save its nfsnode.
1319284741Sdavidcs				 * Since the nfsnode does not have a lock, its
1320284741Sdavidcs				 * vnode lock has to be carried over.
1321284741Sdavidcs				 */
1322284741Sdavidcs				nvp->v_vnlock = vp->v_vnlock;
1323284741Sdavidcs				vp->v_vnlock = NULL;
1324284741Sdavidcs				nvp->v_data = vp->v_data;
1325284741Sdavidcs				vp->v_data = NULL;
1326284741Sdavidcs				vp->v_op = spec_vnodeop_p;
1327284741Sdavidcs				vrele(vp);
1328284741Sdavidcs				vgone(vp);
1329284741Sdavidcs				/*
1330284741Sdavidcs				 * Reinitialize aliased node.
1331284741Sdavidcs				 */
1332284741Sdavidcs				np->n_vnode = nvp;
1333284741Sdavidcs				*vpp = vp = nvp;
1334284741Sdavidcs			}
1335284741Sdavidcs		}
1336284741Sdavidcs		np->n_mtime = mtime.tv_sec;
1337284741Sdavidcs	}
1338284741Sdavidcs	vap = &np->n_vattr;
1339284741Sdavidcs	vap->va_type = vtyp;
1340284741Sdavidcs	vap->va_mode = (vmode & 07777);
1341284741Sdavidcs	vap->va_rdev = rdev;
1342284741Sdavidcs	vap->va_mtime = mtime;
1343284741Sdavidcs	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
1344284741Sdavidcs	if (v3) {
1345284741Sdavidcs		vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
1346284741Sdavidcs		vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
1347284741Sdavidcs		vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
1348284741Sdavidcs		vap->va_size = fxdr_hyper(&fp->fa3_size);
1349284741Sdavidcs		vap->va_blocksize = NFS_FABLKSIZE;
1350284741Sdavidcs		vap->va_bytes = fxdr_hyper(&fp->fa3_used);
1351284741Sdavidcs		vap->va_fileid = fxdr_unsigned(int32_t,
1352284741Sdavidcs		    fp->fa3_fileid.nfsuquad[1]);
1353284741Sdavidcs		fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime);
1354284741Sdavidcs		fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime);
1355284741Sdavidcs		vap->va_flags = 0;
1356284741Sdavidcs		vap->va_filerev = 0;
1357284741Sdavidcs	} else {
1358284741Sdavidcs		vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
1359284741Sdavidcs		vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
1360284741Sdavidcs		vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
1361284741Sdavidcs		vap->va_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
1362284741Sdavidcs		vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
1363284741Sdavidcs		vap->va_bytes = (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks)
1364284741Sdavidcs		    * NFS_FABLKSIZE;
1365284741Sdavidcs		vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
1366284741Sdavidcs		fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
1367284741Sdavidcs		vap->va_flags = 0;
1368284741Sdavidcs		vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t,
1369284741Sdavidcs		    fp->fa2_ctime.nfsv2_sec);
1370284741Sdavidcs		vap->va_ctime.tv_nsec = 0;
1371284741Sdavidcs		vap->va_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
1372284741Sdavidcs		vap->va_filerev = 0;
1373284741Sdavidcs	}
1374284741Sdavidcs	if (vap->va_size != np->n_size) {
1375284741Sdavidcs		if (vap->va_type == VREG) {
1376284741Sdavidcs			if (np->n_flag & NMODIFIED) {
1377284741Sdavidcs				if (vap->va_size < np->n_size)
1378284741Sdavidcs					vap->va_size = np->n_size;
1379284741Sdavidcs				else
1380284741Sdavidcs					np->n_size = vap->va_size;
1381284741Sdavidcs			} else
1382284741Sdavidcs				np->n_size = vap->va_size;
1383284741Sdavidcs			vnode_pager_setsize(vp, np->n_size);
1384284741Sdavidcs		} else
1385284741Sdavidcs			np->n_size = vap->va_size;
1386284741Sdavidcs	}
1387284741Sdavidcs	np->n_attrstamp = time_second;
1388284741Sdavidcs	if (vaper != NULL) {
1389284741Sdavidcs		bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
1390284741Sdavidcs		if (np->n_flag & NCHG) {
1391284741Sdavidcs			if (np->n_flag & NACC)
1392284741Sdavidcs				vaper->va_atime = np->n_atim;
1393284741Sdavidcs			if (np->n_flag & NUPD)
1394284741Sdavidcs				vaper->va_mtime = np->n_mtim;
1395284741Sdavidcs		}
1396284741Sdavidcs	}
1397284741Sdavidcs	return (0);
1398284741Sdavidcs}
1399284741Sdavidcs
1400284741Sdavidcs#ifdef NFS_ACDEBUG
1401284741Sdavidcs#include <sys/sysctl.h>
1402284741SdavidcsSYSCTL_DECL(_vfs_nfs);
1403284741Sdavidcsstatic int nfs_acdebug;
1404284741SdavidcsSYSCTL_INT(_vfs_nfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
1405284741Sdavidcs#endif
1406284741Sdavidcs
1407284741Sdavidcs/*
1408284741Sdavidcs * Check the time stamp
1409284741Sdavidcs * If the cache is valid, copy contents to *vap and return 0
1410284741Sdavidcs * otherwise return an error
1411284741Sdavidcs */
1412284741Sdavidcsint
1413284741Sdavidcsnfs_getattrcache(vp, vaper)
1414284741Sdavidcs	register struct vnode *vp;
1415284741Sdavidcs	struct vattr *vaper;
1416284741Sdavidcs{
1417284741Sdavidcs	register struct nfsnode *np;
1418284741Sdavidcs	register struct vattr *vap;
1419284741Sdavidcs	struct nfsmount *nmp;
1420284741Sdavidcs	int timeo;
1421284741Sdavidcs
1422284741Sdavidcs	np = VTONFS(vp);
1423284741Sdavidcs	vap = &np->n_vattr;
1424284741Sdavidcs	nmp = VFSTONFS(vp->v_mount);
1425284741Sdavidcs	/* XXX n_mtime doesn't seem to be updated on a miss-and-reload */
1426284741Sdavidcs	timeo = (time_second - np->n_mtime) / 10;
1427284741Sdavidcs
1428284741Sdavidcs#ifdef NFS_ACDEBUG
1429284741Sdavidcs	if (nfs_acdebug>1)
1430284741Sdavidcs		printf("nfs_getattrcache: initial timeo = %d\n", timeo);
1431284741Sdavidcs#endif
1432284741Sdavidcs
1433284741Sdavidcs	if (vap->va_type == VDIR) {
1434284741Sdavidcs		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin)
1435284741Sdavidcs			timeo = nmp->nm_acdirmin;
1436284741Sdavidcs		else if (timeo > nmp->nm_acdirmax)
1437284741Sdavidcs			timeo = nmp->nm_acdirmax;
1438284741Sdavidcs	} else {
1439284741Sdavidcs		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin)
1440284741Sdavidcs			timeo = nmp->nm_acregmin;
1441284741Sdavidcs		else if (timeo > nmp->nm_acregmax)
1442284741Sdavidcs			timeo = nmp->nm_acregmax;
1443284741Sdavidcs	}
1444284741Sdavidcs
1445284741Sdavidcs#ifdef NFS_ACDEBUG
1446284741Sdavidcs	if (nfs_acdebug > 2)
1447284741Sdavidcs		printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
1448284741Sdavidcs			nmp->nm_acregmin, nmp->nm_acregmax,
1449284741Sdavidcs			nmp->nm_acdirmin, nmp->nm_acdirmax);
1450284741Sdavidcs
1451284741Sdavidcs	if (nfs_acdebug)
1452284741Sdavidcs		printf("nfs_getattrcache: age = %d; final timeo = %d\n",
1453284741Sdavidcs			(time_second - np->n_attrstamp), timeo);
1454284741Sdavidcs#endif
1455284741Sdavidcs
1456284741Sdavidcs	if ((time_second - np->n_attrstamp) >= timeo) {
1457284741Sdavidcs		nfsstats.attrcache_misses++;
1458284741Sdavidcs		return (ENOENT);
1459284741Sdavidcs	}
1460284741Sdavidcs	nfsstats.attrcache_hits++;
1461284741Sdavidcs	if (vap->va_size != np->n_size) {
1462284741Sdavidcs		if (vap->va_type == VREG) {
1463284741Sdavidcs			if (np->n_flag & NMODIFIED) {
1464284741Sdavidcs				if (vap->va_size < np->n_size)
1465284741Sdavidcs					vap->va_size = np->n_size;
1466284741Sdavidcs				else
1467284741Sdavidcs					np->n_size = vap->va_size;
1468284741Sdavidcs			} else
1469284741Sdavidcs				np->n_size = vap->va_size;
1470284741Sdavidcs			vnode_pager_setsize(vp, np->n_size);
1471284741Sdavidcs		} else
1472284741Sdavidcs			np->n_size = vap->va_size;
1473284741Sdavidcs	}
1474284741Sdavidcs	bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
1475284741Sdavidcs	if (np->n_flag & NCHG) {
1476284741Sdavidcs		if (np->n_flag & NACC)
1477284741Sdavidcs			vaper->va_atime = np->n_atim;
1478284741Sdavidcs		if (np->n_flag & NUPD)
1479284741Sdavidcs			vaper->va_mtime = np->n_mtim;
1480284741Sdavidcs	}
1481284741Sdavidcs	return (0);
1482284741Sdavidcs}
1483284741Sdavidcs
1484284741Sdavidcs#ifndef NFS_NOSERVER
1485284741Sdavidcs/*
1486284741Sdavidcs * Set up nameidata for a lookup() call and do it.
1487284741Sdavidcs *
1488284741Sdavidcs * If pubflag is set, this call is done for a lookup operation on the
1489284741Sdavidcs * public filehandle. In that case we allow crossing mountpoints and
1490284741Sdavidcs * absolute pathnames. However, the caller is expected to check that
1491284741Sdavidcs * the lookup result is within the public fs, and deny access if
1492284741Sdavidcs * it is not.
1493284741Sdavidcs *
1494284741Sdavidcs * nfs_namei() clears out garbage fields that namei() might leave garbage.
1495284741Sdavidcs * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
1496284741Sdavidcs * error occurs but the parent was not requested.
1497284741Sdavidcs *
1498284741Sdavidcs * dirp may be set whether an error is returned or not, and must be
1499284741Sdavidcs * released by the caller.
1500284741Sdavidcs */
1501284741Sdavidcsint
1502284741Sdavidcsnfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag, pubflag)
1503284741Sdavidcs	register struct nameidata *ndp;
1504284741Sdavidcs	fhandle_t *fhp;
1505284741Sdavidcs	int len;
1506284741Sdavidcs	struct nfssvc_sock *slp;
1507284741Sdavidcs	struct sockaddr *nam;
1508284741Sdavidcs	struct mbuf **mdp;
1509284741Sdavidcs	caddr_t *dposp;
1510284741Sdavidcs	struct vnode **retdirp;
1511284741Sdavidcs	struct proc *p;
1512284741Sdavidcs	int kerbflag, pubflag;
1513284741Sdavidcs{
1514284741Sdavidcs	register int i, rem;
1515284741Sdavidcs	register struct mbuf *md;
1516284741Sdavidcs	register char *fromcp, *tocp, *cp;
1517284741Sdavidcs	struct iovec aiov;
1518284741Sdavidcs	struct uio auio;
1519284741Sdavidcs	struct vnode *dp;
1520284741Sdavidcs	int error, rdonly, linklen;
1521284741Sdavidcs	struct componentname *cnp = &ndp->ni_cnd;
1522284741Sdavidcs
1523284741Sdavidcs	*retdirp = (struct vnode *)0;
1524284741Sdavidcs	cnp->cn_pnbuf = zalloc(namei_zone);
1525284741Sdavidcs
1526284741Sdavidcs	/*
1527284741Sdavidcs	 * Copy the name from the mbuf list to ndp->ni_pnbuf
1528284741Sdavidcs	 * and set the various ndp fields appropriately.
1529284741Sdavidcs	 */
1530284741Sdavidcs	fromcp = *dposp;
1531284741Sdavidcs	tocp = cnp->cn_pnbuf;
1532284741Sdavidcs	md = *mdp;
1533284741Sdavidcs	rem = mtod(md, caddr_t) + md->m_len - fromcp;
1534284741Sdavidcs	cnp->cn_hash = 0;
1535284741Sdavidcs	for (i = 0; i < len; i++) {
1536284741Sdavidcs		while (rem == 0) {
1537284741Sdavidcs			md = md->m_next;
1538284741Sdavidcs			if (md == NULL) {
1539284741Sdavidcs				error = EBADRPC;
1540284741Sdavidcs				goto out;
1541284741Sdavidcs			}
1542284741Sdavidcs			fromcp = mtod(md, caddr_t);
1543284741Sdavidcs			rem = md->m_len;
1544284741Sdavidcs		}
1545284741Sdavidcs		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
1546284741Sdavidcs			error = EACCES;
1547284741Sdavidcs			goto out;
1548284741Sdavidcs		}
1549284741Sdavidcs		cnp->cn_hash += (unsigned char)*fromcp;
1550284741Sdavidcs		*tocp++ = *fromcp++;
1551284741Sdavidcs		rem--;
1552284741Sdavidcs	}
1553284741Sdavidcs	*tocp = '\0';
1554284741Sdavidcs	*mdp = md;
1555284741Sdavidcs	*dposp = fromcp;
1556284741Sdavidcs	len = nfsm_rndup(len)-len;
1557284741Sdavidcs	if (len > 0) {
1558284741Sdavidcs		if (rem >= len)
1559284741Sdavidcs			*dposp += len;
1560284741Sdavidcs		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
1561284741Sdavidcs			goto out;
1562284741Sdavidcs	}
1563284741Sdavidcs
1564284741Sdavidcs	/*
1565284741Sdavidcs	 * Extract and set starting directory.
1566284741Sdavidcs	 */
1567284741Sdavidcs	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
1568284741Sdavidcs	    nam, &rdonly, kerbflag, pubflag);
1569284741Sdavidcs	if (error)
1570284741Sdavidcs		goto out;
1571284741Sdavidcs	if (dp->v_type != VDIR) {
1572284741Sdavidcs		vrele(dp);
1573284741Sdavidcs		error = ENOTDIR;
1574284741Sdavidcs		goto out;
1575284741Sdavidcs	}
1576284741Sdavidcs
1577284741Sdavidcs	if (rdonly)
1578284741Sdavidcs		cnp->cn_flags |= RDONLY;
1579284741Sdavidcs
1580284741Sdavidcs	/*
1581284741Sdavidcs	 * Set return directory.  Reference to dp is implicitly transfered
1582284741Sdavidcs	 * to the returned pointer
1583284741Sdavidcs	 */
1584284741Sdavidcs	*retdirp = dp;
1585284741Sdavidcs
1586284741Sdavidcs	if (pubflag) {
1587284741Sdavidcs		/*
1588284741Sdavidcs		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
1589284741Sdavidcs		 * and the 'native path' indicator.
1590284741Sdavidcs		 */
1591284741Sdavidcs		cp = zalloc(namei_zone);
1592284741Sdavidcs		fromcp = cnp->cn_pnbuf;
1593284741Sdavidcs		tocp = cp;
1594284741Sdavidcs		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
1595284741Sdavidcs			switch ((unsigned char)*fromcp) {
1596284741Sdavidcs			case WEBNFS_NATIVE_CHAR:
1597284741Sdavidcs				/*
1598284741Sdavidcs				 * 'Native' path for us is the same
1599284741Sdavidcs				 * as a path according to the NFS spec,
1600284741Sdavidcs				 * just skip the escape char.
1601284741Sdavidcs				 */
1602284741Sdavidcs				fromcp++;
1603284741Sdavidcs				break;
1604284741Sdavidcs			/*
1605284741Sdavidcs			 * More may be added in the future, range 0x80-0xff
1606284741Sdavidcs			 */
1607284741Sdavidcs			default:
1608284741Sdavidcs				error = EIO;
1609284741Sdavidcs				zfree(namei_zone, cp);
1610284741Sdavidcs				goto out;
1611284741Sdavidcs			}
1612284741Sdavidcs		}
1613284741Sdavidcs		/*
1614284741Sdavidcs		 * Translate the '%' escapes, URL-style.
1615284741Sdavidcs		 */
1616284741Sdavidcs		while (*fromcp != '\0') {
1617284741Sdavidcs			if (*fromcp == WEBNFS_ESC_CHAR) {
1618284741Sdavidcs				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
1619284741Sdavidcs					fromcp++;
1620284741Sdavidcs					*tocp++ = HEXSTRTOI(fromcp);
1621284741Sdavidcs					fromcp += 2;
1622284741Sdavidcs					continue;
1623284741Sdavidcs				} else {
1624284741Sdavidcs					error = ENOENT;
1625284741Sdavidcs					zfree(namei_zone, cp);
1626284741Sdavidcs					goto out;
1627284741Sdavidcs				}
1628284741Sdavidcs			} else
1629284741Sdavidcs				*tocp++ = *fromcp++;
1630284741Sdavidcs		}
1631284741Sdavidcs		*tocp = '\0';
1632284741Sdavidcs		zfree(namei_zone, cnp->cn_pnbuf);
1633284741Sdavidcs		cnp->cn_pnbuf = cp;
1634284741Sdavidcs	}
1635284741Sdavidcs
1636284741Sdavidcs	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
1637284741Sdavidcs	ndp->ni_segflg = UIO_SYSSPACE;
1638284741Sdavidcs
1639284741Sdavidcs	if (pubflag) {
1640284741Sdavidcs		ndp->ni_rootdir = rootvnode;
1641284741Sdavidcs		ndp->ni_loopcnt = 0;
1642284741Sdavidcs		if (cnp->cn_pnbuf[0] == '/')
1643284741Sdavidcs			dp = rootvnode;
1644284741Sdavidcs	} else {
1645284741Sdavidcs		cnp->cn_flags |= NOCROSSMOUNT;
1646284741Sdavidcs	}
1647284741Sdavidcs
1648284741Sdavidcs	/*
1649284741Sdavidcs	 * Initialize for scan, set ni_startdir and bump ref on dp again
1650284741Sdavidcs	 * becuase lookup() will dereference ni_startdir.
1651284741Sdavidcs	 */
1652284741Sdavidcs
1653284741Sdavidcs	cnp->cn_proc = p;
1654284741Sdavidcs	VREF(dp);
1655284741Sdavidcs	ndp->ni_startdir = dp;
1656284741Sdavidcs
1657284741Sdavidcs	for (;;) {
1658284741Sdavidcs		cnp->cn_nameptr = cnp->cn_pnbuf;
1659284741Sdavidcs		/*
1660284741Sdavidcs		 * Call lookup() to do the real work.  If an error occurs,
1661284741Sdavidcs		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
1662284741Sdavidcs		 * we do not have to dereference anything before returning.
1663284741Sdavidcs		 * In either case ni_startdir will be dereferenced and NULLed
1664284741Sdavidcs		 * out.
1665284741Sdavidcs		 */
1666284741Sdavidcs		error = lookup(ndp);
1667284741Sdavidcs		if (error)
1668284741Sdavidcs			break;
1669284741Sdavidcs
1670284741Sdavidcs		/*
1671284741Sdavidcs		 * Check for encountering a symbolic link.  Trivial
1672284741Sdavidcs		 * termination occurs if no symlink encountered.
1673284741Sdavidcs		 * Note: zfree is safe because error is 0, so we will
1674284741Sdavidcs		 * not zfree it again when we break.
1675284741Sdavidcs		 */
1676284741Sdavidcs		if ((cnp->cn_flags & ISSYMLINK) == 0) {
1677284741Sdavidcs			nfsrv_object_create(ndp->ni_vp);
1678284741Sdavidcs			if (cnp->cn_flags & (SAVENAME | SAVESTART))
1679284741Sdavidcs				cnp->cn_flags |= HASBUF;
1680284741Sdavidcs			else
1681284741Sdavidcs				zfree(namei_zone, cnp->cn_pnbuf);
1682284741Sdavidcs			break;
1683284741Sdavidcs		}
1684284741Sdavidcs
1685284741Sdavidcs		/*
1686284741Sdavidcs		 * Validate symlink
1687284741Sdavidcs		 */
1688284741Sdavidcs		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
1689284741Sdavidcs			VOP_UNLOCK(ndp->ni_dvp, 0, p);
1690284741Sdavidcs		if (!pubflag) {
1691284741Sdavidcs			error = EINVAL;
1692284741Sdavidcs			goto badlink2;
1693284741Sdavidcs		}
1694284741Sdavidcs
1695284741Sdavidcs		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
1696284741Sdavidcs			error = ELOOP;
1697284741Sdavidcs			goto badlink2;
1698284741Sdavidcs		}
1699284741Sdavidcs		if (ndp->ni_pathlen > 1)
1700284741Sdavidcs			cp = zalloc(namei_zone);
1701284741Sdavidcs		else
1702284741Sdavidcs			cp = cnp->cn_pnbuf;
1703284741Sdavidcs		aiov.iov_base = cp;
1704284741Sdavidcs		aiov.iov_len = MAXPATHLEN;
1705284741Sdavidcs		auio.uio_iov = &aiov;
1706284741Sdavidcs		auio.uio_iovcnt = 1;
1707284741Sdavidcs		auio.uio_offset = 0;
1708284741Sdavidcs		auio.uio_rw = UIO_READ;
1709284741Sdavidcs		auio.uio_segflg = UIO_SYSSPACE;
1710284741Sdavidcs		auio.uio_procp = (struct proc *)0;
1711284741Sdavidcs		auio.uio_resid = MAXPATHLEN;
1712284741Sdavidcs		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
1713284741Sdavidcs		if (error) {
1714284741Sdavidcs		badlink1:
1715284741Sdavidcs			if (ndp->ni_pathlen > 1)
1716284741Sdavidcs				zfree(namei_zone, cp);
1717284741Sdavidcs		badlink2:
1718284741Sdavidcs			vrele(ndp->ni_dvp);
1719284741Sdavidcs			vput(ndp->ni_vp);
1720284741Sdavidcs			break;
1721284741Sdavidcs		}
1722284741Sdavidcs		linklen = MAXPATHLEN - auio.uio_resid;
1723284741Sdavidcs		if (linklen == 0) {
1724284741Sdavidcs			error = ENOENT;
1725284741Sdavidcs			goto badlink1;
1726284741Sdavidcs		}
1727284741Sdavidcs		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
1728284741Sdavidcs			error = ENAMETOOLONG;
1729284741Sdavidcs			goto badlink1;
1730284741Sdavidcs		}
1731284741Sdavidcs
1732284741Sdavidcs		/*
1733284741Sdavidcs		 * Adjust or replace path
1734284741Sdavidcs		 */
1735284741Sdavidcs		if (ndp->ni_pathlen > 1) {
1736284741Sdavidcs			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
1737284741Sdavidcs			zfree(namei_zone, cnp->cn_pnbuf);
1738284741Sdavidcs			cnp->cn_pnbuf = cp;
1739284741Sdavidcs		} else
1740284741Sdavidcs			cnp->cn_pnbuf[linklen] = '\0';
1741284741Sdavidcs		ndp->ni_pathlen += linklen;
1742284741Sdavidcs
1743284741Sdavidcs		/*
1744284741Sdavidcs		 * Cleanup refs for next loop and check if root directory
1745284741Sdavidcs		 * should replace current directory.  Normally ni_dvp
1746284741Sdavidcs		 * becomes the new base directory and is cleaned up when
1747284741Sdavidcs		 * we loop.  Explicitly null pointers after invalidation
1748284741Sdavidcs		 * to clarify operation.
1749284741Sdavidcs		 */
1750284741Sdavidcs		vput(ndp->ni_vp);
1751284741Sdavidcs		ndp->ni_vp = NULL;
1752284741Sdavidcs
1753284741Sdavidcs		if (cnp->cn_pnbuf[0] == '/') {
1754284741Sdavidcs			vrele(ndp->ni_dvp);
1755284741Sdavidcs			ndp->ni_dvp = ndp->ni_rootdir;
1756284741Sdavidcs			VREF(ndp->ni_dvp);
1757284741Sdavidcs		}
1758284741Sdavidcs		ndp->ni_startdir = ndp->ni_dvp;
1759284741Sdavidcs		ndp->ni_dvp = NULL;
1760284741Sdavidcs	}
1761284741Sdavidcs
1762284741Sdavidcs	/*
1763284741Sdavidcs	 * nfs_namei() guarentees that fields will not contain garbage
1764284741Sdavidcs	 * whether an error occurs or not.  This allows the caller to track
1765284741Sdavidcs	 * cleanup state trivially.
1766284741Sdavidcs	 */
1767284741Sdavidcsout:
1768284741Sdavidcs	if (error) {
1769284741Sdavidcs		zfree(namei_zone, cnp->cn_pnbuf);
1770284741Sdavidcs		ndp->ni_vp = NULL;
1771284741Sdavidcs		ndp->ni_dvp = NULL;
1772284741Sdavidcs		ndp->ni_startdir = NULL;
1773284741Sdavidcs		cnp->cn_flags &= ~HASBUF;
1774284741Sdavidcs	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
1775284741Sdavidcs		ndp->ni_dvp = NULL;
1776284741Sdavidcs	}
1777284741Sdavidcs	return (error);
1778284741Sdavidcs}
1779284741Sdavidcs
1780284741Sdavidcs/*
1781284741Sdavidcs * A fiddled version of m_adj() that ensures null fill to a long
1782284741Sdavidcs * boundary and only trims off the back end
1783284741Sdavidcs */
1784284741Sdavidcsvoid
1785284741Sdavidcsnfsm_adj(mp, len, nul)
1786284741Sdavidcs	struct mbuf *mp;
1787284741Sdavidcs	register int len;
1788284741Sdavidcs	int nul;
1789284741Sdavidcs{
1790284741Sdavidcs	register struct mbuf *m;
1791284741Sdavidcs	register int count, i;
1792284741Sdavidcs	register char *cp;
1793284741Sdavidcs
1794284741Sdavidcs	/*
1795284741Sdavidcs	 * Trim from tail.  Scan the mbuf chain,
1796284741Sdavidcs	 * calculating its length and finding the last mbuf.
1797284741Sdavidcs	 * If the adjustment only affects this mbuf, then just
1798284741Sdavidcs	 * adjust and return.  Otherwise, rescan and truncate
1799284741Sdavidcs	 * after the remaining size.
1800284741Sdavidcs	 */
1801284741Sdavidcs	count = 0;
1802284741Sdavidcs	m = mp;
1803284741Sdavidcs	for (;;) {
1804284741Sdavidcs		count += m->m_len;
1805284741Sdavidcs		if (m->m_next == (struct mbuf *)0)
1806284741Sdavidcs			break;
1807284741Sdavidcs		m = m->m_next;
1808284741Sdavidcs	}
1809284741Sdavidcs	if (m->m_len > len) {
1810284741Sdavidcs		m->m_len -= len;
1811284741Sdavidcs		if (nul > 0) {
1812284741Sdavidcs			cp = mtod(m, caddr_t)+m->m_len-nul;
1813284741Sdavidcs			for (i = 0; i < nul; i++)
1814284741Sdavidcs				*cp++ = '\0';
1815284741Sdavidcs		}
1816284741Sdavidcs		return;
1817284741Sdavidcs	}
1818284741Sdavidcs	count -= len;
1819284741Sdavidcs	if (count < 0)
1820284741Sdavidcs		count = 0;
1821284741Sdavidcs	/*
1822284741Sdavidcs	 * Correct length for chain is "count".
1823284741Sdavidcs	 * Find the mbuf with last data, adjust its length,
1824284741Sdavidcs	 * and toss data from remaining mbufs on chain.
1825284741Sdavidcs	 */
1826284741Sdavidcs	for (m = mp; m; m = m->m_next) {
1827284741Sdavidcs		if (m->m_len >= count) {
1828284741Sdavidcs			m->m_len = count;
1829284741Sdavidcs			if (nul > 0) {
1830284741Sdavidcs				cp = mtod(m, caddr_t)+m->m_len-nul;
1831284741Sdavidcs				for (i = 0; i < nul; i++)
1832284741Sdavidcs					*cp++ = '\0';
1833284741Sdavidcs			}
1834284741Sdavidcs			break;
1835284741Sdavidcs		}
1836284741Sdavidcs		count -= m->m_len;
1837284741Sdavidcs	}
1838284741Sdavidcs	for (m = m->m_next;m;m = m->m_next)
1839284741Sdavidcs		m->m_len = 0;
1840284741Sdavidcs}
1841284741Sdavidcs
1842284741Sdavidcs/*
1843284741Sdavidcs * Make these functions instead of macros, so that the kernel text size
1844284741Sdavidcs * doesn't get too big...
1845284741Sdavidcs */
1846284741Sdavidcsvoid
1847284741Sdavidcsnfsm_srvwcc(nfsd, before_ret, before_vap, after_ret, after_vap, mbp, bposp)
1848284741Sdavidcs	struct nfsrv_descript *nfsd;
1849284741Sdavidcs	int before_ret;
1850284741Sdavidcs	register struct vattr *before_vap;
1851284741Sdavidcs	int after_ret;
1852284741Sdavidcs	struct vattr *after_vap;
1853284741Sdavidcs	struct mbuf **mbp;
1854284741Sdavidcs	char **bposp;
1855284741Sdavidcs{
1856284741Sdavidcs	register struct mbuf *mb = *mbp, *mb2;
1857284741Sdavidcs	register char *bpos = *bposp;
1858284741Sdavidcs	register u_int32_t *tl;
1859284741Sdavidcs
1860284741Sdavidcs	if (before_ret) {
1861284741Sdavidcs		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
1862284741Sdavidcs		*tl = nfs_false;
1863284741Sdavidcs	} else {
1864284741Sdavidcs		nfsm_build(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
1865284741Sdavidcs		*tl++ = nfs_true;
1866284741Sdavidcs		txdr_hyper(before_vap->va_size, tl);
1867284741Sdavidcs		tl += 2;
1868284741Sdavidcs		txdr_nfsv3time(&(before_vap->va_mtime), tl);
1869284741Sdavidcs		tl += 2;
1870284741Sdavidcs		txdr_nfsv3time(&(before_vap->va_ctime), tl);
1871284741Sdavidcs	}
1872284741Sdavidcs	*bposp = bpos;
1873284741Sdavidcs	*mbp = mb;
1874284741Sdavidcs	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
1875284741Sdavidcs}
1876284741Sdavidcs
1877284741Sdavidcsvoid
1878284741Sdavidcsnfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp)
1879284741Sdavidcs	struct nfsrv_descript *nfsd;
1880284741Sdavidcs	int after_ret;
1881284741Sdavidcs	struct vattr *after_vap;
1882284741Sdavidcs	struct mbuf **mbp;
1883284741Sdavidcs	char **bposp;
1884284741Sdavidcs{
1885284741Sdavidcs	register struct mbuf *mb = *mbp, *mb2;
1886284741Sdavidcs	register char *bpos = *bposp;
1887284741Sdavidcs	register u_int32_t *tl;
1888284741Sdavidcs	register struct nfs_fattr *fp;
1889284741Sdavidcs
1890284741Sdavidcs	if (after_ret) {
1891284741Sdavidcs		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
1892284741Sdavidcs		*tl = nfs_false;
1893284741Sdavidcs	} else {
1894284741Sdavidcs		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
1895284741Sdavidcs		*tl++ = nfs_true;
1896284741Sdavidcs		fp = (struct nfs_fattr *)tl;
1897284741Sdavidcs		nfsm_srvfattr(nfsd, after_vap, fp);
1898284741Sdavidcs	}
1899284741Sdavidcs	*mbp = mb;
1900284741Sdavidcs	*bposp = bpos;
1901284741Sdavidcs}
1902284741Sdavidcs
1903284741Sdavidcsvoid
1904284741Sdavidcsnfsm_srvfattr(nfsd, vap, fp)
1905284741Sdavidcs	register struct nfsrv_descript *nfsd;
1906284741Sdavidcs	register struct vattr *vap;
1907284741Sdavidcs	register struct nfs_fattr *fp;
1908284741Sdavidcs{
1909284741Sdavidcs
1910284741Sdavidcs	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
1911284741Sdavidcs	fp->fa_uid = txdr_unsigned(vap->va_uid);
1912284741Sdavidcs	fp->fa_gid = txdr_unsigned(vap->va_gid);
1913284741Sdavidcs	if (nfsd->nd_flag & ND_NFSV3) {
1914284741Sdavidcs		fp->fa_type = vtonfsv3_type(vap->va_type);
1915284741Sdavidcs		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
1916284741Sdavidcs		txdr_hyper(vap->va_size, &fp->fa3_size);
1917284741Sdavidcs		txdr_hyper(vap->va_bytes, &fp->fa3_used);
1918284741Sdavidcs		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
1919284741Sdavidcs		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
1920284741Sdavidcs		fp->fa3_fsid.nfsuquad[0] = 0;
1921284741Sdavidcs		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
1922284741Sdavidcs		fp->fa3_fileid.nfsuquad[0] = 0;
1923284741Sdavidcs		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
1924284741Sdavidcs		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
1925284741Sdavidcs		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
1926284741Sdavidcs		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
1927284741Sdavidcs	} else {
1928284741Sdavidcs		fp->fa_type = vtonfsv2_type(vap->va_type);
1929284741Sdavidcs		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
1930284741Sdavidcs		fp->fa2_size = txdr_unsigned(vap->va_size);
1931284741Sdavidcs		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
1932284741Sdavidcs		if (vap->va_type == VFIFO)
1933284741Sdavidcs			fp->fa2_rdev = 0xffffffff;
1934284741Sdavidcs		else
1935284741Sdavidcs			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
1936284741Sdavidcs		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
1937284741Sdavidcs		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
1938284741Sdavidcs		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
1939284741Sdavidcs		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
1940284741Sdavidcs		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
1941284741Sdavidcs		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
1942284741Sdavidcs	}
1943284741Sdavidcs}
1944284741Sdavidcs
1945284741Sdavidcs/*
1946284741Sdavidcs * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
1947284741Sdavidcs * 	- look up fsid in mount list (if not found ret error)
1948284741Sdavidcs *	- get vp and export rights by calling VFS_FHTOVP()
1949284741Sdavidcs *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
1950284741Sdavidcs *	- if not lockflag unlock it with VOP_UNLOCK()
1951284741Sdavidcs */
1952284741Sdavidcsint
1953284741Sdavidcsnfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag, pubflag)
1954284741Sdavidcs	fhandle_t *fhp;
1955284741Sdavidcs	int lockflag;
1956284741Sdavidcs	struct vnode **vpp;
1957284741Sdavidcs	struct ucred *cred;
1958284741Sdavidcs	struct nfssvc_sock *slp;
1959284741Sdavidcs	struct sockaddr *nam;
1960284741Sdavidcs	int *rdonlyp;
1961284741Sdavidcs	int kerbflag;
1962284741Sdavidcs	int pubflag;
1963284741Sdavidcs{
1964284741Sdavidcs	struct proc *p = curproc; /* XXX */
1965284741Sdavidcs	register struct mount *mp;
1966284741Sdavidcs	register int i;
1967284741Sdavidcs	struct ucred *credanon;
1968284741Sdavidcs	int error, exflags;
1969284741Sdavidcs#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
1970284741Sdavidcs	struct sockaddr_int *saddr;
1971284741Sdavidcs#endif
1972284741Sdavidcs
1973284741Sdavidcs	*vpp = (struct vnode *)0;
1974284741Sdavidcs
1975284741Sdavidcs	if (nfs_ispublicfh(fhp)) {
1976284741Sdavidcs		if (!pubflag || !nfs_pub.np_valid)
1977284741Sdavidcs			return (ESTALE);
1978284741Sdavidcs		fhp = &nfs_pub.np_handle;
1979284741Sdavidcs	}
1980284741Sdavidcs
1981284741Sdavidcs	mp = vfs_getvfs(&fhp->fh_fsid);
1982284741Sdavidcs	if (!mp)
1983284741Sdavidcs		return (ESTALE);
1984284741Sdavidcs	error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon);
1985284741Sdavidcs	if (error)
1986284741Sdavidcs		return (error);
1987284741Sdavidcs#ifdef MNT_EXNORESPORT
1988284741Sdavidcs	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
1989284741Sdavidcs		saddr = (struct sockaddr_in *)nam;
1990284741Sdavidcs		if (saddr->sin_family == AF_INET &&
1991284741Sdavidcs		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
1992284741Sdavidcs			vput(*vpp);
1993284741Sdavidcs			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
1994284741Sdavidcs		}
1995284741Sdavidcs	}
1996284741Sdavidcs#endif
1997284741Sdavidcs	/*
1998284741Sdavidcs	 * Check/setup credentials.
1999284741Sdavidcs	 */
2000284741Sdavidcs	if (exflags & MNT_EXKERB) {
2001284741Sdavidcs		if (!kerbflag) {
2002284741Sdavidcs			vput(*vpp);
2003284741Sdavidcs			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
2004284741Sdavidcs		}
2005284741Sdavidcs	} else if (kerbflag) {
2006284741Sdavidcs		vput(*vpp);
2007284741Sdavidcs		return (NFSERR_AUTHERR | AUTH_TOOWEAK);
2008284741Sdavidcs	} else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
2009284741Sdavidcs		cred->cr_uid = credanon->cr_uid;
2010284741Sdavidcs		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
2011284741Sdavidcs			cred->cr_groups[i] = credanon->cr_groups[i];
2012284741Sdavidcs		cred->cr_ngroups = i;
2013284741Sdavidcs	}
2014284741Sdavidcs	if (exflags & MNT_EXRDONLY)
2015284741Sdavidcs		*rdonlyp = 1;
2016284741Sdavidcs	else
2017284741Sdavidcs		*rdonlyp = 0;
2018284741Sdavidcs
2019284741Sdavidcs	nfsrv_object_create(*vpp);
2020284741Sdavidcs
2021284741Sdavidcs	if (!lockflag)
2022284741Sdavidcs		VOP_UNLOCK(*vpp, 0, p);
2023284741Sdavidcs	return (0);
2024284741Sdavidcs}
2025284741Sdavidcs
2026284741Sdavidcs
2027284741Sdavidcs/*
2028284741Sdavidcs * WebNFS: check if a filehandle is a public filehandle. For v3, this
2029284741Sdavidcs * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
2030284741Sdavidcs * transformed this to all zeroes in both cases, so check for it.
2031284741Sdavidcs */
2032284741Sdavidcsint
2033284741Sdavidcsnfs_ispublicfh(fhp)
2034284741Sdavidcs	fhandle_t *fhp;
2035284741Sdavidcs{
2036284741Sdavidcs	char *cp = (char *)fhp;
2037284741Sdavidcs	int i;
2038284741Sdavidcs
2039284741Sdavidcs	for (i = 0; i < NFSX_V3FH; i++)
2040284741Sdavidcs		if (*cp++ != 0)
2041284741Sdavidcs			return (FALSE);
2042284741Sdavidcs	return (TRUE);
2043284741Sdavidcs}
2044284741Sdavidcs
2045284741Sdavidcs#endif /* NFS_NOSERVER */
2046284741Sdavidcs/*
2047284741Sdavidcs * This function compares two net addresses by family and returns TRUE
2048284741Sdavidcs * if they are the same host.
2049284741Sdavidcs * If there is any doubt, return FALSE.
2050284741Sdavidcs * The AF_INET family is handled as a special case so that address mbufs
2051284741Sdavidcs * don't need to be saved to store "struct in_addr", which is only 4 bytes.
2052284741Sdavidcs */
2053284741Sdavidcsint
2054284741Sdavidcsnetaddr_match(family, haddr, nam)
2055284741Sdavidcs	int family;
2056284741Sdavidcs	union nethostaddr *haddr;
2057284741Sdavidcs	struct sockaddr *nam;
2058284741Sdavidcs{
2059284741Sdavidcs	register struct sockaddr_in *inetaddr;
2060284741Sdavidcs
2061284741Sdavidcs	switch (family) {
2062284741Sdavidcs	case AF_INET:
2063284741Sdavidcs		inetaddr = (struct sockaddr_in *)nam;
2064284741Sdavidcs		if (inetaddr->sin_family == AF_INET &&
2065284741Sdavidcs		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
2066284741Sdavidcs			return (1);
2067284741Sdavidcs		break;
2068284741Sdavidcs#ifdef ISO
2069284741Sdavidcs	case AF_ISO:
2070284741Sdavidcs	    {
2071284741Sdavidcs		register struct sockaddr_iso *isoaddr1, *isoaddr2;
2072284741Sdavidcs
2073284741Sdavidcs		isoaddr1 = (struct sockaddr_iso *)nam;
2074284741Sdavidcs		isoaddr2 = (struct sockaddr_iso *)haddr->had_nam;
2075284741Sdavidcs		if (isoaddr1->siso_family == AF_ISO &&
2076284741Sdavidcs		    isoaddr1->siso_nlen > 0 &&
2077284741Sdavidcs		    isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
2078284741Sdavidcs		    SAME_ISOADDR(isoaddr1, isoaddr2))
2079284741Sdavidcs			return (1);
2080284741Sdavidcs		break;
2081284741Sdavidcs	    }
2082284741Sdavidcs#endif	/* ISO */
2083284741Sdavidcs	default:
2084284741Sdavidcs		break;
2085284741Sdavidcs	};
2086284741Sdavidcs	return (0);
2087284741Sdavidcs}
2088284741Sdavidcs
2089284741Sdavidcsstatic nfsuint64 nfs_nullcookie = { { 0, 0 } };
2090284741Sdavidcs/*
2091284741Sdavidcs * This function finds the directory cookie that corresponds to the
2092284741Sdavidcs * logical byte offset given.
2093284741Sdavidcs */
2094284741Sdavidcsnfsuint64 *
2095284741Sdavidcsnfs_getcookie(np, off, add)
2096284741Sdavidcs	register struct nfsnode *np;
2097284741Sdavidcs	off_t off;
2098284741Sdavidcs	int add;
2099284741Sdavidcs{
2100284741Sdavidcs	register struct nfsdmap *dp, *dp2;
2101284741Sdavidcs	register int pos;
2102284741Sdavidcs
2103284741Sdavidcs	pos = (uoff_t)off / NFS_DIRBLKSIZ;
2104284741Sdavidcs	if (pos == 0 || off < 0) {
2105284741Sdavidcs#ifdef DIAGNOSTIC
2106284741Sdavidcs		if (add)
2107284741Sdavidcs			panic("nfs getcookie add at <= 0");
2108284741Sdavidcs#endif
2109284741Sdavidcs		return (&nfs_nullcookie);
2110284741Sdavidcs	}
2111284741Sdavidcs	pos--;
2112284741Sdavidcs	dp = np->n_cookies.lh_first;
2113284741Sdavidcs	if (!dp) {
2114284741Sdavidcs		if (add) {
2115284741Sdavidcs			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
2116284741Sdavidcs				M_NFSDIROFF, M_WAITOK);
2117284741Sdavidcs			dp->ndm_eocookie = 0;
2118284741Sdavidcs			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
2119284741Sdavidcs		} else
2120284741Sdavidcs			return ((nfsuint64 *)0);
2121284741Sdavidcs	}
2122284741Sdavidcs	while (pos >= NFSNUMCOOKIES) {
2123284741Sdavidcs		pos -= NFSNUMCOOKIES;
2124284741Sdavidcs		if (dp->ndm_list.le_next) {
2125284741Sdavidcs			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
2126284741Sdavidcs				pos >= dp->ndm_eocookie)
2127284741Sdavidcs				return ((nfsuint64 *)0);
2128284741Sdavidcs			dp = dp->ndm_list.le_next;
2129284741Sdavidcs		} else if (add) {
2130284741Sdavidcs			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
2131284741Sdavidcs				M_NFSDIROFF, M_WAITOK);
2132284741Sdavidcs			dp2->ndm_eocookie = 0;
2133284741Sdavidcs			LIST_INSERT_AFTER(dp, dp2, ndm_list);
2134284741Sdavidcs			dp = dp2;
2135284741Sdavidcs		} else
2136284741Sdavidcs			return ((nfsuint64 *)0);
2137284741Sdavidcs	}
2138284741Sdavidcs	if (pos >= dp->ndm_eocookie) {
2139284741Sdavidcs		if (add)
2140284741Sdavidcs			dp->ndm_eocookie = pos + 1;
2141284741Sdavidcs		else
2142284741Sdavidcs			return ((nfsuint64 *)0);
2143284741Sdavidcs	}
2144284741Sdavidcs	return (&dp->ndm_cookies[pos]);
2145284741Sdavidcs}
2146284741Sdavidcs
2147284741Sdavidcs/*
2148284741Sdavidcs * Invalidate cached directory information, except for the actual directory
2149284741Sdavidcs * blocks (which are invalidated separately).
2150284741Sdavidcs * Done mainly to avoid the use of stale offset cookies.
2151284741Sdavidcs */
2152284741Sdavidcsvoid
2153284741Sdavidcsnfs_invaldir(vp)
2154284741Sdavidcs	register struct vnode *vp;
2155284741Sdavidcs{
2156284741Sdavidcs	register struct nfsnode *np = VTONFS(vp);
2157284741Sdavidcs
2158284741Sdavidcs#ifdef DIAGNOSTIC
2159284741Sdavidcs	if (vp->v_type != VDIR)
2160284741Sdavidcs		panic("nfs: invaldir not dir");
2161284741Sdavidcs#endif
2162284741Sdavidcs	np->n_direofoffset = 0;
2163284741Sdavidcs	np->n_cookieverf.nfsuquad[0] = 0;
2164284741Sdavidcs	np->n_cookieverf.nfsuquad[1] = 0;
2165284741Sdavidcs	if (np->n_cookies.lh_first)
2166284741Sdavidcs		np->n_cookies.lh_first->ndm_eocookie = 0;
2167284741Sdavidcs}
2168284741Sdavidcs
2169284741Sdavidcs/*
2170284741Sdavidcs * The write verifier has changed (probably due to a server reboot), so all
2171284741Sdavidcs * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
2172284741Sdavidcs * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
2173284741Sdavidcs * flag. Once done the new write verifier can be set for the mount point.
2174284741Sdavidcs */
2175284741Sdavidcsvoid
2176284741Sdavidcsnfs_clearcommit(mp)
2177284741Sdavidcs	struct mount *mp;
2178284741Sdavidcs{
2179284741Sdavidcs	register struct vnode *vp, *nvp;
2180284741Sdavidcs	register struct buf *bp, *nbp;
2181284741Sdavidcs	int s;
2182284741Sdavidcs
2183284741Sdavidcs	s = splbio();
2184284741Sdavidcsloop:
2185284741Sdavidcs	for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
2186284741Sdavidcs		if (vp->v_mount != mp)	/* Paranoia */
2187284741Sdavidcs			goto loop;
2188284741Sdavidcs		nvp = vp->v_mntvnodes.le_next;
2189284741Sdavidcs		for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
2190284741Sdavidcs			nbp = TAILQ_NEXT(bp, b_vnbufs);
2191284741Sdavidcs			if (BUF_REFCNT(bp) == 0 &&
2192284741Sdavidcs			    (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
2193284741Sdavidcs				== (B_DELWRI | B_NEEDCOMMIT))
2194284741Sdavidcs				bp->b_flags &= ~B_NEEDCOMMIT;
2195284741Sdavidcs		}
2196284741Sdavidcs	}
2197284741Sdavidcs	splx(s);
2198284741Sdavidcs}
2199284741Sdavidcs
2200284741Sdavidcs#ifndef NFS_NOSERVER
2201284741Sdavidcs/*
2202284741Sdavidcs * Map errnos to NFS error numbers. For Version 3 also filter out error
2203284741Sdavidcs * numbers not specified for the associated procedure.
2204284741Sdavidcs */
2205284741Sdavidcsint
2206284741Sdavidcsnfsrv_errmap(nd, err)
2207284741Sdavidcs	struct nfsrv_descript *nd;
2208284741Sdavidcs	register int err;
2209284741Sdavidcs{
2210284741Sdavidcs	register short *defaulterrp, *errp;
2211284741Sdavidcs
2212284741Sdavidcs	if (nd->nd_flag & ND_NFSV3) {
2213284741Sdavidcs	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
2214284741Sdavidcs		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
2215284741Sdavidcs		while (*++errp) {
2216284741Sdavidcs			if (*errp == err)
2217284741Sdavidcs				return (err);
2218284741Sdavidcs			else if (*errp > err)
2219284741Sdavidcs				break;
2220284741Sdavidcs		}
2221284741Sdavidcs		return ((int)*defaulterrp);
2222284741Sdavidcs	    } else
2223284741Sdavidcs		return (err & 0xffff);
2224284741Sdavidcs	}
2225284741Sdavidcs	if (err <= ELAST)
2226284741Sdavidcs		return ((int)nfsrv_v2errmap[err - 1]);
2227284741Sdavidcs	return (NFSERR_IO);
2228284741Sdavidcs}
2229284741Sdavidcs
2230284741Sdavidcsint
2231284741Sdavidcsnfsrv_object_create(vp)
2232284741Sdavidcs	struct vnode *vp;
2233284741Sdavidcs{
2234284741Sdavidcs
2235284741Sdavidcs	if (vp == NULL || vp->v_type != VREG)
2236284741Sdavidcs		return (1);
2237284741Sdavidcs	return (vfs_object_create(vp, curproc,
2238284741Sdavidcs				  curproc ? curproc->p_ucred : NULL));
2239284741Sdavidcs}
2240284741Sdavidcs
2241284741Sdavidcs/*
2242284741Sdavidcs * Sort the group list in increasing numerical order.
2243284741Sdavidcs * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
2244284741Sdavidcs *  that used to be here.)
2245284741Sdavidcs */
2246284741Sdavidcsvoid
2247284741Sdavidcsnfsrvw_sort(list, num)
2248284741Sdavidcs        register gid_t *list;
2249284741Sdavidcs        register int num;
2250284741Sdavidcs{
2251284741Sdavidcs	register int i, j;
2252284741Sdavidcs	gid_t v;
2253284741Sdavidcs
2254284741Sdavidcs	/* Insertion sort. */
2255284741Sdavidcs	for (i = 1; i < num; i++) {
2256284741Sdavidcs		v = list[i];
2257284741Sdavidcs		/* find correct slot for value v, moving others up */
2258284741Sdavidcs		for (j = i; --j >= 0 && v < list[j];)
2259284741Sdavidcs			list[j + 1] = list[j];
2260284741Sdavidcs		list[j + 1] = v;
2261284741Sdavidcs	}
2262284741Sdavidcs}
2263284741Sdavidcs
2264284741Sdavidcs/*
2265284741Sdavidcs * copy credentials making sure that the result can be compared with bcmp().
2266284741Sdavidcs */
2267284741Sdavidcsvoid
2268284741Sdavidcsnfsrv_setcred(incred, outcred)
2269284741Sdavidcs	register struct ucred *incred, *outcred;
2270284741Sdavidcs{
2271284741Sdavidcs	register int i;
2272284741Sdavidcs
2273284741Sdavidcs	bzero((caddr_t)outcred, sizeof (struct ucred));
2274284741Sdavidcs	outcred->cr_ref = 1;
2275284741Sdavidcs	outcred->cr_uid = incred->cr_uid;
2276284741Sdavidcs	outcred->cr_ngroups = incred->cr_ngroups;
2277284741Sdavidcs	for (i = 0; i < incred->cr_ngroups; i++)
2278284741Sdavidcs		outcred->cr_groups[i] = incred->cr_groups[i];
2279284741Sdavidcs	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
2280284741Sdavidcs}
2281284741Sdavidcs#endif /* NFS_NOSERVER */
2282284741Sdavidcs