nfs_srvsubs.c revision 48274
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1989, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software contributed to Berkeley by
61541Srgrimes * Rick Macklem at The University of Guelph.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 3. All advertising materials mentioning features or use of this software
171541Srgrimes *    must display the following acknowledgement:
181541Srgrimes *	This product includes software developed by the University of
191541Srgrimes *	California, Berkeley and its contributors.
201541Srgrimes * 4. Neither the name of the University nor the names of its contributors
211541Srgrimes *    may be used to endorse or promote products derived from this software
221541Srgrimes *    without specific prior written permission.
231541Srgrimes *
241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341541Srgrimes * SUCH DAMAGE.
351541Srgrimes *
3636503Speter *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
3748274Speter * $Id: nfs_subs.c,v 1.77 1999/06/26 02:46:30 mckusick Exp $
381541Srgrimes */
391541Srgrimes
401541Srgrimes/*
411541Srgrimes * These functions support the macros and help fiddle mbuf chains for
421541Srgrimes * the nfs op functions. They do things like create the rpc header and
431541Srgrimes * copy data between mbuf chains and uio lists.
441541Srgrimes */
451541Srgrimes#include <sys/param.h>
4648274Speter#include <sys/systm.h>
4748274Speter#include <sys/kernel.h>
4831886Sbde#include <sys/buf.h>
491541Srgrimes#include <sys/proc.h>
501541Srgrimes#include <sys/mount.h>
511541Srgrimes#include <sys/vnode.h>
521541Srgrimes#include <sys/namei.h>
531541Srgrimes#include <sys/mbuf.h>
541541Srgrimes#include <sys/socket.h>
551541Srgrimes#include <sys/stat.h>
569336Sdfr#include <sys/malloc.h>
572997Swollman#include <sys/sysent.h>
582997Swollman#include <sys/syscall.h>
591541Srgrimes
603305Sphk#include <vm/vm.h>
6112662Sdg#include <vm/vm_object.h>
6212662Sdg#include <vm/vm_extern.h>
6332011Sbde#include <vm/vm_zone.h>
643305Sphk
651541Srgrimes#include <nfs/rpcv2.h>
669336Sdfr#include <nfs/nfsproto.h>
6730808Sbde#include <nfs/nfs.h>
681541Srgrimes#include <nfs/nfsnode.h>
691541Srgrimes#include <nfs/xdr_subs.h>
701541Srgrimes#include <nfs/nfsm_subs.h>
711541Srgrimes#include <nfs/nfsmount.h>
721541Srgrimes#include <nfs/nqnfs.h>
731541Srgrimes#include <nfs/nfsrtt.h>
741541Srgrimes
751541Srgrimes#include <miscfs/specfs/specdev.h>
761541Srgrimes
771541Srgrimes#include <netinet/in.h>
781541Srgrimes#ifdef ISO
791541Srgrimes#include <netiso/iso.h>
801541Srgrimes#endif
811541Srgrimes
821541Srgrimes/*
831541Srgrimes * Data items converted to xdr at startup, since they are constant
841541Srgrimes * This is kinda hokey, but may save a little time doing byte swaps
851541Srgrimes */
8636541Speteru_int32_t nfs_xdrneg1;
8736541Speteru_int32_t rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
889336Sdfr	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted,
891541Srgrimes	rpc_auth_kerb;
9036541Speteru_int32_t nfs_prog, nqnfs_prog, nfs_true, nfs_false;
911541Srgrimes
921541Srgrimes/* And other global data */
9336541Speterstatic u_int32_t nfs_xid = 0;
9412911Sphkstatic enum vtype nv2tov_type[8]= {
9512911Sphk	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON,  VNON
9612911Sphk};
9712911Sphkenum vtype nv3tov_type[8]= {
9812911Sphk	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO
9912911Sphk};
10012911Sphk
1019336Sdfrint nfs_ticks;
10242957Sdillonint nfs_pbuf_freecnt = -1;	/* start out unlimited */
1039336Sdfr
1049759Sbdestruct nfs_reqq nfs_reqq;
1059759Sbdestruct nfssvc_sockhead nfssvc_sockhead;
1069759Sbdeint nfssvc_sockhead_flag;
1079759Sbdestruct nfsd_head nfsd_head;
1089759Sbdeint nfsd_head_flag;
1099759Sbdestruct nfs_bufq nfs_bufq;
1109759Sbdestruct nqtimerhead nqtimerhead;
1119759Sbdestruct nqfhhashhead *nqfhhashtbl;
1129759Sbdeu_long nqfhhash;
1139759Sbde
11438894Sbdestatic void (*nfs_prev_lease_updatetime) __P((int));
11538894Sbdestatic int nfs_prev_nfssvc_sy_narg;
11638894Sbdestatic sy_call_t *nfs_prev_nfssvc_sy_call;
11738894Sbde
11813416Sphk#ifndef NFS_NOSERVER
11938894Sbde
12038894Sbdestatic vop_t *nfs_prev_vop_lease_check;
12138894Sbdestatic int nfs_prev_getfh_sy_narg;
12238894Sbdestatic sy_call_t *nfs_prev_getfh_sy_call;
12338894Sbde
1249336Sdfr/*
1259336Sdfr * Mapping of old NFS Version 2 RPC numbers to generic numbers.
1269336Sdfr */
1279336Sdfrint nfsv3_procid[NFS_NPROCS] = {
1289336Sdfr	NFSPROC_NULL,
1299336Sdfr	NFSPROC_GETATTR,
1309336Sdfr	NFSPROC_SETATTR,
1319336Sdfr	NFSPROC_NOOP,
1329336Sdfr	NFSPROC_LOOKUP,
1339336Sdfr	NFSPROC_READLINK,
1349336Sdfr	NFSPROC_READ,
1359336Sdfr	NFSPROC_NOOP,
1369336Sdfr	NFSPROC_WRITE,
1379336Sdfr	NFSPROC_CREATE,
1389336Sdfr	NFSPROC_REMOVE,
1399336Sdfr	NFSPROC_RENAME,
1409336Sdfr	NFSPROC_LINK,
1419336Sdfr	NFSPROC_SYMLINK,
1429336Sdfr	NFSPROC_MKDIR,
1439336Sdfr	NFSPROC_RMDIR,
1449336Sdfr	NFSPROC_READDIR,
1459336Sdfr	NFSPROC_FSSTAT,
1469336Sdfr	NFSPROC_NOOP,
1479336Sdfr	NFSPROC_NOOP,
1489336Sdfr	NFSPROC_NOOP,
1499336Sdfr	NFSPROC_NOOP,
1509336Sdfr	NFSPROC_NOOP,
1519336Sdfr	NFSPROC_NOOP,
1529336Sdfr	NFSPROC_NOOP,
1539336Sdfr	NFSPROC_NOOP
1549336Sdfr};
1559336Sdfr
15613416Sphk#endif /* NFS_NOSERVER */
1579336Sdfr/*
1589336Sdfr * and the reverse mapping from generic to Version 2 procedure numbers
1599336Sdfr */
1609336Sdfrint nfsv2_procid[NFS_NPROCS] = {
1619336Sdfr	NFSV2PROC_NULL,
1629336Sdfr	NFSV2PROC_GETATTR,
1639336Sdfr	NFSV2PROC_SETATTR,
1649336Sdfr	NFSV2PROC_LOOKUP,
1659336Sdfr	NFSV2PROC_NOOP,
1669336Sdfr	NFSV2PROC_READLINK,
1679336Sdfr	NFSV2PROC_READ,
1689336Sdfr	NFSV2PROC_WRITE,
1699336Sdfr	NFSV2PROC_CREATE,
1709336Sdfr	NFSV2PROC_MKDIR,
1719336Sdfr	NFSV2PROC_SYMLINK,
1729336Sdfr	NFSV2PROC_CREATE,
1739336Sdfr	NFSV2PROC_REMOVE,
1749336Sdfr	NFSV2PROC_RMDIR,
1759336Sdfr	NFSV2PROC_RENAME,
1769336Sdfr	NFSV2PROC_LINK,
1779336Sdfr	NFSV2PROC_READDIR,
1789336Sdfr	NFSV2PROC_NOOP,
1799336Sdfr	NFSV2PROC_STATFS,
1809336Sdfr	NFSV2PROC_NOOP,
1819336Sdfr	NFSV2PROC_NOOP,
1829336Sdfr	NFSV2PROC_NOOP,
1839336Sdfr	NFSV2PROC_NOOP,
1849336Sdfr	NFSV2PROC_NOOP,
1859336Sdfr	NFSV2PROC_NOOP,
1869336Sdfr	NFSV2PROC_NOOP,
1879336Sdfr};
1889336Sdfr
18913416Sphk#ifndef NFS_NOSERVER
1909336Sdfr/*
1919336Sdfr * Maps errno values to nfs error numbers.
1929336Sdfr * Use NFSERR_IO as the catch all for ones not specifically defined in
1939336Sdfr * RFC 1094.
1949336Sdfr */
1959336Sdfrstatic u_char nfsrv_v2errmap[ELAST] = {
1969336Sdfr  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1979336Sdfr  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1989336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
1999336Sdfr  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
2009336Sdfr  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2019336Sdfr  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
2029336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2039336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2049336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2059336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2069336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2079336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2089336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_NAMETOL,	NFSERR_IO,	NFSERR_IO,
2099336Sdfr  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
2109336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
2119336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
21241796Sdt  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
21341796Sdt  NFSERR_IO /* << Last is 86 */
2149336Sdfr};
2159336Sdfr
2169336Sdfr/*
2179336Sdfr * Maps errno values to nfs error numbers.
2189336Sdfr * Although it is not obvious whether or not NFS clients really care if
2199336Sdfr * a returned error value is in the specified list for the procedure, the
2209336Sdfr * safest thing to do is filter them appropriately. For Version 2, the
2219336Sdfr * X/Open XNFS document is the only specification that defines error values
2229336Sdfr * for each RPC (The RFC simply lists all possible error values for all RPCs),
2239336Sdfr * so I have decided to not do this for Version 2.
2249336Sdfr * The first entry is the default error return and the rest are the valid
2259336Sdfr * errors for that RPC in increasing numeric order.
2269336Sdfr */
2279336Sdfrstatic short nfsv3err_null[] = {
2289336Sdfr	0,
2299336Sdfr	0,
2309336Sdfr};
2319336Sdfr
2329336Sdfrstatic short nfsv3err_getattr[] = {
2339336Sdfr	NFSERR_IO,
2349336Sdfr	NFSERR_IO,
2359336Sdfr	NFSERR_STALE,
2369336Sdfr	NFSERR_BADHANDLE,
2379336Sdfr	NFSERR_SERVERFAULT,
2389336Sdfr	0,
2399336Sdfr};
2409336Sdfr
2419336Sdfrstatic short nfsv3err_setattr[] = {
2429336Sdfr	NFSERR_IO,
2439336Sdfr	NFSERR_PERM,
2449336Sdfr	NFSERR_IO,
2459336Sdfr	NFSERR_ACCES,
2469336Sdfr	NFSERR_INVAL,
2479336Sdfr	NFSERR_NOSPC,
2489336Sdfr	NFSERR_ROFS,
2499336Sdfr	NFSERR_DQUOT,
2509336Sdfr	NFSERR_STALE,
2519336Sdfr	NFSERR_BADHANDLE,
2529336Sdfr	NFSERR_NOT_SYNC,
2539336Sdfr	NFSERR_SERVERFAULT,
2549336Sdfr	0,
2559336Sdfr};
2569336Sdfr
2579336Sdfrstatic short nfsv3err_lookup[] = {
2589336Sdfr	NFSERR_IO,
2599336Sdfr	NFSERR_NOENT,
2609336Sdfr	NFSERR_IO,
2619336Sdfr	NFSERR_ACCES,
2629336Sdfr	NFSERR_NOTDIR,
2639336Sdfr	NFSERR_NAMETOL,
2649336Sdfr	NFSERR_STALE,
2659336Sdfr	NFSERR_BADHANDLE,
2669336Sdfr	NFSERR_SERVERFAULT,
2679336Sdfr	0,
2689336Sdfr};
2699336Sdfr
2709336Sdfrstatic short nfsv3err_access[] = {
2719336Sdfr	NFSERR_IO,
2729336Sdfr	NFSERR_IO,
2739336Sdfr	NFSERR_STALE,
2749336Sdfr	NFSERR_BADHANDLE,
2759336Sdfr	NFSERR_SERVERFAULT,
2769336Sdfr	0,
2779336Sdfr};
2789336Sdfr
2799336Sdfrstatic short nfsv3err_readlink[] = {
2809336Sdfr	NFSERR_IO,
2819336Sdfr	NFSERR_IO,
2829336Sdfr	NFSERR_ACCES,
2839336Sdfr	NFSERR_INVAL,
2849336Sdfr	NFSERR_STALE,
2859336Sdfr	NFSERR_BADHANDLE,
2869336Sdfr	NFSERR_NOTSUPP,
2879336Sdfr	NFSERR_SERVERFAULT,
2889336Sdfr	0,
2899336Sdfr};
2909336Sdfr
2919336Sdfrstatic short nfsv3err_read[] = {
2929336Sdfr	NFSERR_IO,
2939336Sdfr	NFSERR_IO,
2949336Sdfr	NFSERR_NXIO,
2959336Sdfr	NFSERR_ACCES,
2969336Sdfr	NFSERR_INVAL,
2979336Sdfr	NFSERR_STALE,
2989336Sdfr	NFSERR_BADHANDLE,
2999336Sdfr	NFSERR_SERVERFAULT,
3009336Sdfr	0,
3019336Sdfr};
3029336Sdfr
3039336Sdfrstatic short nfsv3err_write[] = {
3049336Sdfr	NFSERR_IO,
3059336Sdfr	NFSERR_IO,
3069336Sdfr	NFSERR_ACCES,
3079336Sdfr	NFSERR_INVAL,
3089336Sdfr	NFSERR_FBIG,
3099336Sdfr	NFSERR_NOSPC,
3109336Sdfr	NFSERR_ROFS,
3119336Sdfr	NFSERR_DQUOT,
3129336Sdfr	NFSERR_STALE,
3139336Sdfr	NFSERR_BADHANDLE,
3149336Sdfr	NFSERR_SERVERFAULT,
3159336Sdfr	0,
3169336Sdfr};
3179336Sdfr
3189336Sdfrstatic short nfsv3err_create[] = {
3199336Sdfr	NFSERR_IO,
3209336Sdfr	NFSERR_IO,
3219336Sdfr	NFSERR_ACCES,
3229336Sdfr	NFSERR_EXIST,
3239336Sdfr	NFSERR_NOTDIR,
3249336Sdfr	NFSERR_NOSPC,
3259336Sdfr	NFSERR_ROFS,
3269336Sdfr	NFSERR_NAMETOL,
3279336Sdfr	NFSERR_DQUOT,
3289336Sdfr	NFSERR_STALE,
3299336Sdfr	NFSERR_BADHANDLE,
3309336Sdfr	NFSERR_NOTSUPP,
3319336Sdfr	NFSERR_SERVERFAULT,
3329336Sdfr	0,
3339336Sdfr};
3349336Sdfr
3359336Sdfrstatic short nfsv3err_mkdir[] = {
3369336Sdfr	NFSERR_IO,
3379336Sdfr	NFSERR_IO,
3389336Sdfr	NFSERR_ACCES,
3399336Sdfr	NFSERR_EXIST,
3409336Sdfr	NFSERR_NOTDIR,
3419336Sdfr	NFSERR_NOSPC,
3429336Sdfr	NFSERR_ROFS,
3439336Sdfr	NFSERR_NAMETOL,
3449336Sdfr	NFSERR_DQUOT,
3459336Sdfr	NFSERR_STALE,
3469336Sdfr	NFSERR_BADHANDLE,
3479336Sdfr	NFSERR_NOTSUPP,
3489336Sdfr	NFSERR_SERVERFAULT,
3499336Sdfr	0,
3509336Sdfr};
3519336Sdfr
3529336Sdfrstatic short nfsv3err_symlink[] = {
3539336Sdfr	NFSERR_IO,
3549336Sdfr	NFSERR_IO,
3559336Sdfr	NFSERR_ACCES,
3569336Sdfr	NFSERR_EXIST,
3579336Sdfr	NFSERR_NOTDIR,
3589336Sdfr	NFSERR_NOSPC,
3599336Sdfr	NFSERR_ROFS,
3609336Sdfr	NFSERR_NAMETOL,
3619336Sdfr	NFSERR_DQUOT,
3629336Sdfr	NFSERR_STALE,
3639336Sdfr	NFSERR_BADHANDLE,
3649336Sdfr	NFSERR_NOTSUPP,
3659336Sdfr	NFSERR_SERVERFAULT,
3669336Sdfr	0,
3679336Sdfr};
3689336Sdfr
3699336Sdfrstatic short nfsv3err_mknod[] = {
3709336Sdfr	NFSERR_IO,
3719336Sdfr	NFSERR_IO,
3729336Sdfr	NFSERR_ACCES,
3739336Sdfr	NFSERR_EXIST,
3749336Sdfr	NFSERR_NOTDIR,
3759336Sdfr	NFSERR_NOSPC,
3769336Sdfr	NFSERR_ROFS,
3779336Sdfr	NFSERR_NAMETOL,
3789336Sdfr	NFSERR_DQUOT,
3799336Sdfr	NFSERR_STALE,
3809336Sdfr	NFSERR_BADHANDLE,
3819336Sdfr	NFSERR_NOTSUPP,
3829336Sdfr	NFSERR_SERVERFAULT,
3839336Sdfr	NFSERR_BADTYPE,
3849336Sdfr	0,
3859336Sdfr};
3869336Sdfr
3879336Sdfrstatic short nfsv3err_remove[] = {
3889336Sdfr	NFSERR_IO,
3899336Sdfr	NFSERR_NOENT,
3909336Sdfr	NFSERR_IO,
3919336Sdfr	NFSERR_ACCES,
3929336Sdfr	NFSERR_NOTDIR,
3939336Sdfr	NFSERR_ROFS,
3949336Sdfr	NFSERR_NAMETOL,
3959336Sdfr	NFSERR_STALE,
3969336Sdfr	NFSERR_BADHANDLE,
3979336Sdfr	NFSERR_SERVERFAULT,
3989336Sdfr	0,
3999336Sdfr};
4009336Sdfr
4019336Sdfrstatic short nfsv3err_rmdir[] = {
4029336Sdfr	NFSERR_IO,
4039336Sdfr	NFSERR_NOENT,
4049336Sdfr	NFSERR_IO,
4059336Sdfr	NFSERR_ACCES,
4069336Sdfr	NFSERR_EXIST,
4079336Sdfr	NFSERR_NOTDIR,
4089336Sdfr	NFSERR_INVAL,
4099336Sdfr	NFSERR_ROFS,
4109336Sdfr	NFSERR_NAMETOL,
4119336Sdfr	NFSERR_NOTEMPTY,
4129336Sdfr	NFSERR_STALE,
4139336Sdfr	NFSERR_BADHANDLE,
4149336Sdfr	NFSERR_NOTSUPP,
4159336Sdfr	NFSERR_SERVERFAULT,
4169336Sdfr	0,
4179336Sdfr};
4189336Sdfr
4199336Sdfrstatic short nfsv3err_rename[] = {
4209336Sdfr	NFSERR_IO,
4219336Sdfr	NFSERR_NOENT,
4229336Sdfr	NFSERR_IO,
4239336Sdfr	NFSERR_ACCES,
4249336Sdfr	NFSERR_EXIST,
4259336Sdfr	NFSERR_XDEV,
4269336Sdfr	NFSERR_NOTDIR,
4279336Sdfr	NFSERR_ISDIR,
4289336Sdfr	NFSERR_INVAL,
4299336Sdfr	NFSERR_NOSPC,
4309336Sdfr	NFSERR_ROFS,
4319336Sdfr	NFSERR_MLINK,
4329336Sdfr	NFSERR_NAMETOL,
4339336Sdfr	NFSERR_NOTEMPTY,
4349336Sdfr	NFSERR_DQUOT,
4359336Sdfr	NFSERR_STALE,
4369336Sdfr	NFSERR_BADHANDLE,
4379336Sdfr	NFSERR_NOTSUPP,
4389336Sdfr	NFSERR_SERVERFAULT,
4399336Sdfr	0,
4409336Sdfr};
4419336Sdfr
4429336Sdfrstatic short nfsv3err_link[] = {
4439336Sdfr	NFSERR_IO,
4449336Sdfr	NFSERR_IO,
4459336Sdfr	NFSERR_ACCES,
4469336Sdfr	NFSERR_EXIST,
4479336Sdfr	NFSERR_XDEV,
4489336Sdfr	NFSERR_NOTDIR,
4499336Sdfr	NFSERR_INVAL,
4509336Sdfr	NFSERR_NOSPC,
4519336Sdfr	NFSERR_ROFS,
4529336Sdfr	NFSERR_MLINK,
4539336Sdfr	NFSERR_NAMETOL,
4549336Sdfr	NFSERR_DQUOT,
4559336Sdfr	NFSERR_STALE,
4569336Sdfr	NFSERR_BADHANDLE,
4579336Sdfr	NFSERR_NOTSUPP,
4589336Sdfr	NFSERR_SERVERFAULT,
4599336Sdfr	0,
4609336Sdfr};
4619336Sdfr
4629336Sdfrstatic short nfsv3err_readdir[] = {
4639336Sdfr	NFSERR_IO,
4649336Sdfr	NFSERR_IO,
4659336Sdfr	NFSERR_ACCES,
4669336Sdfr	NFSERR_NOTDIR,
4679336Sdfr	NFSERR_STALE,
4689336Sdfr	NFSERR_BADHANDLE,
4699336Sdfr	NFSERR_BAD_COOKIE,
4709336Sdfr	NFSERR_TOOSMALL,
4719336Sdfr	NFSERR_SERVERFAULT,
4729336Sdfr	0,
4739336Sdfr};
4749336Sdfr
4759336Sdfrstatic short nfsv3err_readdirplus[] = {
4769336Sdfr	NFSERR_IO,
4779336Sdfr	NFSERR_IO,
4789336Sdfr	NFSERR_ACCES,
4799336Sdfr	NFSERR_NOTDIR,
4809336Sdfr	NFSERR_STALE,
4819336Sdfr	NFSERR_BADHANDLE,
4829336Sdfr	NFSERR_BAD_COOKIE,
4839336Sdfr	NFSERR_NOTSUPP,
4849336Sdfr	NFSERR_TOOSMALL,
4859336Sdfr	NFSERR_SERVERFAULT,
4869336Sdfr	0,
4879336Sdfr};
4889336Sdfr
4899336Sdfrstatic short nfsv3err_fsstat[] = {
4909336Sdfr	NFSERR_IO,
4919336Sdfr	NFSERR_IO,
4929336Sdfr	NFSERR_STALE,
4939336Sdfr	NFSERR_BADHANDLE,
4949336Sdfr	NFSERR_SERVERFAULT,
4959336Sdfr	0,
4969336Sdfr};
4979336Sdfr
4989336Sdfrstatic short nfsv3err_fsinfo[] = {
4999336Sdfr	NFSERR_STALE,
5009336Sdfr	NFSERR_STALE,
5019336Sdfr	NFSERR_BADHANDLE,
5029336Sdfr	NFSERR_SERVERFAULT,
5039336Sdfr	0,
5049336Sdfr};
5059336Sdfr
5069336Sdfrstatic short nfsv3err_pathconf[] = {
5079336Sdfr	NFSERR_STALE,
5089336Sdfr	NFSERR_STALE,
5099336Sdfr	NFSERR_BADHANDLE,
5109336Sdfr	NFSERR_SERVERFAULT,
5119336Sdfr	0,
5129336Sdfr};
5139336Sdfr
5149336Sdfrstatic short nfsv3err_commit[] = {
5159336Sdfr	NFSERR_IO,
5169336Sdfr	NFSERR_IO,
5179336Sdfr	NFSERR_STALE,
5189336Sdfr	NFSERR_BADHANDLE,
5199336Sdfr	NFSERR_SERVERFAULT,
5209336Sdfr	0,
5219336Sdfr};
5229336Sdfr
5239336Sdfrstatic short *nfsrv_v3errmap[] = {
5249336Sdfr	nfsv3err_null,
5259336Sdfr	nfsv3err_getattr,
5269336Sdfr	nfsv3err_setattr,
5279336Sdfr	nfsv3err_lookup,
5289336Sdfr	nfsv3err_access,
5299336Sdfr	nfsv3err_readlink,
5309336Sdfr	nfsv3err_read,
5319336Sdfr	nfsv3err_write,
5329336Sdfr	nfsv3err_create,
5339336Sdfr	nfsv3err_mkdir,
5349336Sdfr	nfsv3err_symlink,
5359336Sdfr	nfsv3err_mknod,
5369336Sdfr	nfsv3err_remove,
5379336Sdfr	nfsv3err_rmdir,
5389336Sdfr	nfsv3err_rename,
5399336Sdfr	nfsv3err_link,
5409336Sdfr	nfsv3err_readdir,
5419336Sdfr	nfsv3err_readdirplus,
5429336Sdfr	nfsv3err_fsstat,
5439336Sdfr	nfsv3err_fsinfo,
5449336Sdfr	nfsv3err_pathconf,
5459336Sdfr	nfsv3err_commit,
5469336Sdfr};
5479336Sdfr
54813416Sphk#endif /* NFS_NOSERVER */
54913416Sphk
5501541Srgrimesextern struct nfsrtt nfsrtt;
5511541Srgrimesextern time_t nqnfsstarttime;
5521541Srgrimesextern int nqsrv_clockskew;
5531541Srgrimesextern int nqsrv_writeslack;
5541541Srgrimesextern int nqsrv_maxlease;
5559336Sdfrextern struct nfsstats nfsstats;
5569336Sdfrextern int nqnfs_piggy[NFS_NPROCS];
5579336Sdfrextern nfstype nfsv2_type[9];
5589336Sdfrextern nfstype nfsv3_type[9];
5599336Sdfrextern struct nfsnodehashhead *nfsnodehashtbl;
5609336Sdfrextern u_long nfsnodehash;
5611541Srgrimes
5622997Swollmanstruct getfh_args;
5632997Swollmanextern int getfh(struct proc *, struct getfh_args *, int *);
5642997Swollmanstruct nfssvc_args;
5652997Swollmanextern int nfssvc(struct proc *, struct nfssvc_args *, int *);
5662997Swollman
5673664SphkLIST_HEAD(nfsnodehashhead, nfsnode);
5683664Sphk
56927446Sdfrint nfs_webnamei __P((struct nameidata *, struct vnode *, struct proc *));
57027446Sdfr
57134961Sphku_quad_t
57234961Sphknfs_curusec()
57334961Sphk{
57434961Sphk	struct timeval tv;
57534961Sphk
57634961Sphk	getmicrotime(&tv);
57734961Sphk	return ((u_quad_t)tv.tv_sec * 1000000 + (u_quad_t)tv.tv_usec);
57834961Sphk}
57934961Sphk
5801541Srgrimes/*
5811541Srgrimes * Create the header for an rpc request packet
5821541Srgrimes * The hsiz is the size of the rest of the nfs request header.
5831541Srgrimes * (just used to decide if a cluster is a good idea)
5841541Srgrimes */
5851541Srgrimesstruct mbuf *
5861541Srgrimesnfsm_reqh(vp, procid, hsiz, bposp)
5871541Srgrimes	struct vnode *vp;
5881541Srgrimes	u_long procid;
5891541Srgrimes	int hsiz;
5901541Srgrimes	caddr_t *bposp;
5911541Srgrimes{
5921541Srgrimes	register struct mbuf *mb;
59336541Speter	register u_int32_t *tl;
5941541Srgrimes	register caddr_t bpos;
5951541Srgrimes	struct mbuf *mb2;
5961541Srgrimes	struct nfsmount *nmp;
5971541Srgrimes	int nqflag;
5981541Srgrimes
5991541Srgrimes	MGET(mb, M_WAIT, MT_DATA);
6001541Srgrimes	if (hsiz >= MINCLSIZE)
6011541Srgrimes		MCLGET(mb, M_WAIT);
6021541Srgrimes	mb->m_len = 0;
6031541Srgrimes	bpos = mtod(mb, caddr_t);
6048876Srgrimes
6051541Srgrimes	/*
6061541Srgrimes	 * For NQNFS, add lease request.
6071541Srgrimes	 */
6081541Srgrimes	if (vp) {
6091541Srgrimes		nmp = VFSTONFS(vp->v_mount);
6101541Srgrimes		if (nmp->nm_flag & NFSMNT_NQNFS) {
6111541Srgrimes			nqflag = NQNFS_NEEDLEASE(vp, procid);
6121541Srgrimes			if (nqflag) {
61336541Speter				nfsm_build(tl, u_int32_t *, 2*NFSX_UNSIGNED);
6141541Srgrimes				*tl++ = txdr_unsigned(nqflag);
6151541Srgrimes				*tl = txdr_unsigned(nmp->nm_leaseterm);
6161541Srgrimes			} else {
61736541Speter				nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
6181541Srgrimes				*tl = 0;
6191541Srgrimes			}
6201541Srgrimes		}
6211541Srgrimes	}
6221541Srgrimes	/* Finally, return values */
6231541Srgrimes	*bposp = bpos;
6241541Srgrimes	return (mb);
6251541Srgrimes}
6261541Srgrimes
6271541Srgrimes/*
6281541Srgrimes * Build the RPC header and fill in the authorization info.
6291541Srgrimes * The authorization string argument is only used when the credentials
6301541Srgrimes * come from outside of the kernel.
6311541Srgrimes * Returns the head of the mbuf list.
6321541Srgrimes */
6331541Srgrimesstruct mbuf *
6349336Sdfrnfsm_rpchead(cr, nmflag, procid, auth_type, auth_len, auth_str, verf_len,
6359336Sdfr	verf_str, mrest, mrest_len, mbp, xidp)
6361541Srgrimes	register struct ucred *cr;
6379336Sdfr	int nmflag;
6381541Srgrimes	int procid;
6391541Srgrimes	int auth_type;
6401541Srgrimes	int auth_len;
6411541Srgrimes	char *auth_str;
6429336Sdfr	int verf_len;
6439336Sdfr	char *verf_str;
6441541Srgrimes	struct mbuf *mrest;
6451541Srgrimes	int mrest_len;
6461541Srgrimes	struct mbuf **mbp;
64736541Speter	u_int32_t *xidp;
6481541Srgrimes{
6491541Srgrimes	register struct mbuf *mb;
65036541Speter	register u_int32_t *tl;
6511541Srgrimes	register caddr_t bpos;
6521541Srgrimes	register int i;
6531541Srgrimes	struct mbuf *mreq, *mb2;
6541541Srgrimes	int siz, grpsiz, authsiz;
6551541Srgrimes
6561541Srgrimes	authsiz = nfsm_rndup(auth_len);
6571541Srgrimes	MGETHDR(mb, M_WAIT, MT_DATA);
6589336Sdfr	if ((authsiz + 10 * NFSX_UNSIGNED) >= MINCLSIZE) {
6591541Srgrimes		MCLGET(mb, M_WAIT);
6609336Sdfr	} else if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) {
6619336Sdfr		MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED);
6621541Srgrimes	} else {
6639336Sdfr		MH_ALIGN(mb, 8 * NFSX_UNSIGNED);
6641541Srgrimes	}
6651541Srgrimes	mb->m_len = 0;
6661541Srgrimes	mreq = mb;
6671541Srgrimes	bpos = mtod(mb, caddr_t);
6681541Srgrimes
6691541Srgrimes	/*
6701541Srgrimes	 * First the RPC header.
6711541Srgrimes	 */
67236541Speter	nfsm_build(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
67317186Sdfr
67435066Sphk	/* Get a pretty random xid to start with */
67535066Sphk	if (!nfs_xid)
67635066Sphk		nfs_xid = random();
67717186Sdfr	/*
67817186Sdfr	 * Skip zero xid if it should ever happen.
67917186Sdfr	 */
6801541Srgrimes	if (++nfs_xid == 0)
6811541Srgrimes		nfs_xid++;
68217186Sdfr
6831541Srgrimes	*tl++ = *xidp = txdr_unsigned(nfs_xid);
6841541Srgrimes	*tl++ = rpc_call;
6851541Srgrimes	*tl++ = rpc_vers;
6869336Sdfr	if (nmflag & NFSMNT_NQNFS) {
6871541Srgrimes		*tl++ = txdr_unsigned(NQNFS_PROG);
6889336Sdfr		*tl++ = txdr_unsigned(NQNFS_VER3);
6891541Srgrimes	} else {
6901541Srgrimes		*tl++ = txdr_unsigned(NFS_PROG);
6919336Sdfr		if (nmflag & NFSMNT_NFSV3)
6929336Sdfr			*tl++ = txdr_unsigned(NFS_VER3);
6939336Sdfr		else
6949336Sdfr			*tl++ = txdr_unsigned(NFS_VER2);
6951541Srgrimes	}
6969336Sdfr	if (nmflag & NFSMNT_NFSV3)
6979336Sdfr		*tl++ = txdr_unsigned(procid);
6989336Sdfr	else
6999336Sdfr		*tl++ = txdr_unsigned(nfsv2_procid[procid]);
7001541Srgrimes
7011541Srgrimes	/*
7021541Srgrimes	 * And then the authorization cred.
7031541Srgrimes	 */
7041541Srgrimes	*tl++ = txdr_unsigned(auth_type);
7051541Srgrimes	*tl = txdr_unsigned(authsiz);
7061541Srgrimes	switch (auth_type) {
7071541Srgrimes	case RPCAUTH_UNIX:
70836541Speter		nfsm_build(tl, u_int32_t *, auth_len);
7091541Srgrimes		*tl++ = 0;		/* stamp ?? */
7101541Srgrimes		*tl++ = 0;		/* NULL hostname */
7111541Srgrimes		*tl++ = txdr_unsigned(cr->cr_uid);
7121541Srgrimes		*tl++ = txdr_unsigned(cr->cr_groups[0]);
7131541Srgrimes		grpsiz = (auth_len >> 2) - 5;
7141541Srgrimes		*tl++ = txdr_unsigned(grpsiz);
7151541Srgrimes		for (i = 1; i <= grpsiz; i++)
7161541Srgrimes			*tl++ = txdr_unsigned(cr->cr_groups[i]);
7171541Srgrimes		break;
7189336Sdfr	case RPCAUTH_KERB4:
7191541Srgrimes		siz = auth_len;
7201541Srgrimes		while (siz > 0) {
7211541Srgrimes			if (M_TRAILINGSPACE(mb) == 0) {
7221541Srgrimes				MGET(mb2, M_WAIT, MT_DATA);
7231541Srgrimes				if (siz >= MINCLSIZE)
7241541Srgrimes					MCLGET(mb2, M_WAIT);
7251541Srgrimes				mb->m_next = mb2;
7261541Srgrimes				mb = mb2;
7271541Srgrimes				mb->m_len = 0;
7281541Srgrimes				bpos = mtod(mb, caddr_t);
7291541Srgrimes			}
7301541Srgrimes			i = min(siz, M_TRAILINGSPACE(mb));
7311541Srgrimes			bcopy(auth_str, bpos, i);
7321541Srgrimes			mb->m_len += i;
7331541Srgrimes			auth_str += i;
7341541Srgrimes			bpos += i;
7351541Srgrimes			siz -= i;
7361541Srgrimes		}
7371541Srgrimes		if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
7381541Srgrimes			for (i = 0; i < siz; i++)
7391541Srgrimes				*bpos++ = '\0';
7401541Srgrimes			mb->m_len += siz;
7411541Srgrimes		}
7421541Srgrimes		break;
7431541Srgrimes	};
7449336Sdfr
7459336Sdfr	/*
7469336Sdfr	 * And the verifier...
7479336Sdfr	 */
74836541Speter	nfsm_build(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
7499336Sdfr	if (verf_str) {
7509336Sdfr		*tl++ = txdr_unsigned(RPCAUTH_KERB4);
7519336Sdfr		*tl = txdr_unsigned(verf_len);
7529336Sdfr		siz = verf_len;
7539336Sdfr		while (siz > 0) {
7549336Sdfr			if (M_TRAILINGSPACE(mb) == 0) {
7559336Sdfr				MGET(mb2, M_WAIT, MT_DATA);
7569336Sdfr				if (siz >= MINCLSIZE)
7579336Sdfr					MCLGET(mb2, M_WAIT);
7589336Sdfr				mb->m_next = mb2;
7599336Sdfr				mb = mb2;
7609336Sdfr				mb->m_len = 0;
7619336Sdfr				bpos = mtod(mb, caddr_t);
7629336Sdfr			}
7639336Sdfr			i = min(siz, M_TRAILINGSPACE(mb));
7649336Sdfr			bcopy(verf_str, bpos, i);
7659336Sdfr			mb->m_len += i;
7669336Sdfr			verf_str += i;
7679336Sdfr			bpos += i;
7689336Sdfr			siz -= i;
7699336Sdfr		}
7709336Sdfr		if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) {
7719336Sdfr			for (i = 0; i < siz; i++)
7729336Sdfr				*bpos++ = '\0';
7739336Sdfr			mb->m_len += siz;
7749336Sdfr		}
7759336Sdfr	} else {
7769336Sdfr		*tl++ = txdr_unsigned(RPCAUTH_NULL);
7779336Sdfr		*tl = 0;
7789336Sdfr	}
7791541Srgrimes	mb->m_next = mrest;
7809336Sdfr	mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
7811541Srgrimes	mreq->m_pkthdr.rcvif = (struct ifnet *)0;
7821541Srgrimes	*mbp = mb;
7831541Srgrimes	return (mreq);
7841541Srgrimes}
7851541Srgrimes
7861541Srgrimes/*
7871541Srgrimes * copies mbuf chain to the uio scatter/gather list
7881541Srgrimes */
7891549Srgrimesint
7901541Srgrimesnfsm_mbuftouio(mrep, uiop, siz, dpos)
7911541Srgrimes	struct mbuf **mrep;
7921541Srgrimes	register struct uio *uiop;
7931541Srgrimes	int siz;
7941541Srgrimes	caddr_t *dpos;
7951541Srgrimes{
7961541Srgrimes	register char *mbufcp, *uiocp;
7971541Srgrimes	register int xfer, left, len;
7981541Srgrimes	register struct mbuf *mp;
7991541Srgrimes	long uiosiz, rem;
8001541Srgrimes	int error = 0;
8011541Srgrimes
8021541Srgrimes	mp = *mrep;
8031541Srgrimes	mbufcp = *dpos;
8041541Srgrimes	len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
8051541Srgrimes	rem = nfsm_rndup(siz)-siz;
8061541Srgrimes	while (siz > 0) {
8071541Srgrimes		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
8081541Srgrimes			return (EFBIG);
8091541Srgrimes		left = uiop->uio_iov->iov_len;
8101541Srgrimes		uiocp = uiop->uio_iov->iov_base;
8111541Srgrimes		if (left > siz)
8121541Srgrimes			left = siz;
8131541Srgrimes		uiosiz = left;
8141541Srgrimes		while (left > 0) {
8151541Srgrimes			while (len == 0) {
8161541Srgrimes				mp = mp->m_next;
8171541Srgrimes				if (mp == NULL)
8181541Srgrimes					return (EBADRPC);
8191541Srgrimes				mbufcp = mtod(mp, caddr_t);
8201541Srgrimes				len = mp->m_len;
8211541Srgrimes			}
8221541Srgrimes			xfer = (left > len) ? len : left;
8231541Srgrimes#ifdef notdef
8241541Srgrimes			/* Not Yet.. */
8251541Srgrimes			if (uiop->uio_iov->iov_op != NULL)
8261541Srgrimes				(*(uiop->uio_iov->iov_op))
8271541Srgrimes				(mbufcp, uiocp, xfer);
8281541Srgrimes			else
8291541Srgrimes#endif
8301541Srgrimes			if (uiop->uio_segflg == UIO_SYSSPACE)
8311541Srgrimes				bcopy(mbufcp, uiocp, xfer);
8321541Srgrimes			else
8331541Srgrimes				copyout(mbufcp, uiocp, xfer);
8341541Srgrimes			left -= xfer;
8351541Srgrimes			len -= xfer;
8361541Srgrimes			mbufcp += xfer;
8371541Srgrimes			uiocp += xfer;
8381541Srgrimes			uiop->uio_offset += xfer;
8391541Srgrimes			uiop->uio_resid -= xfer;
8401541Srgrimes		}
8411541Srgrimes		if (uiop->uio_iov->iov_len <= siz) {
8421541Srgrimes			uiop->uio_iovcnt--;
8431541Srgrimes			uiop->uio_iov++;
8441541Srgrimes		} else {
8451541Srgrimes			uiop->uio_iov->iov_base += uiosiz;
8461541Srgrimes			uiop->uio_iov->iov_len -= uiosiz;
8471541Srgrimes		}
8481541Srgrimes		siz -= uiosiz;
8491541Srgrimes	}
8501541Srgrimes	*dpos = mbufcp;
8511541Srgrimes	*mrep = mp;
8521541Srgrimes	if (rem > 0) {
8531541Srgrimes		if (len < rem)
8541541Srgrimes			error = nfs_adv(mrep, dpos, rem, len);
8551541Srgrimes		else
8561541Srgrimes			*dpos += rem;
8571541Srgrimes	}
8581541Srgrimes	return (error);
8591541Srgrimes}
8601541Srgrimes
8611541Srgrimes/*
86217186Sdfr * copies a uio scatter/gather list to an mbuf chain.
86317186Sdfr * NOTE: can ony handle iovcnt == 1
8641541Srgrimes */
8651549Srgrimesint
8661541Srgrimesnfsm_uiotombuf(uiop, mq, siz, bpos)
8671541Srgrimes	register struct uio *uiop;
8681541Srgrimes	struct mbuf **mq;
8691541Srgrimes	int siz;
8701541Srgrimes	caddr_t *bpos;
8711541Srgrimes{
8721541Srgrimes	register char *uiocp;
8731541Srgrimes	register struct mbuf *mp, *mp2;
8741541Srgrimes	register int xfer, left, mlen;
8751541Srgrimes	int uiosiz, clflg, rem;
8761541Srgrimes	char *cp;
8771541Srgrimes
87836519Speter#ifdef DIAGNOSTIC
87917186Sdfr	if (uiop->uio_iovcnt != 1)
88017186Sdfr		panic("nfsm_uiotombuf: iovcnt != 1");
88136519Speter#endif
88217186Sdfr
8831541Srgrimes	if (siz > MLEN)		/* or should it >= MCLBYTES ?? */
8841541Srgrimes		clflg = 1;
8851541Srgrimes	else
8861541Srgrimes		clflg = 0;
8871541Srgrimes	rem = nfsm_rndup(siz)-siz;
8881541Srgrimes	mp = mp2 = *mq;
8891541Srgrimes	while (siz > 0) {
8901541Srgrimes		left = uiop->uio_iov->iov_len;
8911541Srgrimes		uiocp = uiop->uio_iov->iov_base;
8921541Srgrimes		if (left > siz)
8931541Srgrimes			left = siz;
8941541Srgrimes		uiosiz = left;
8951541Srgrimes		while (left > 0) {
8961541Srgrimes			mlen = M_TRAILINGSPACE(mp);
8971541Srgrimes			if (mlen == 0) {
8981541Srgrimes				MGET(mp, M_WAIT, MT_DATA);
8991541Srgrimes				if (clflg)
9001541Srgrimes					MCLGET(mp, M_WAIT);
9011541Srgrimes				mp->m_len = 0;
9021541Srgrimes				mp2->m_next = mp;
9031541Srgrimes				mp2 = mp;
9041541Srgrimes				mlen = M_TRAILINGSPACE(mp);
9051541Srgrimes			}
9061541Srgrimes			xfer = (left > mlen) ? mlen : left;
9071541Srgrimes#ifdef notdef
9081541Srgrimes			/* Not Yet.. */
9091541Srgrimes			if (uiop->uio_iov->iov_op != NULL)
9101541Srgrimes				(*(uiop->uio_iov->iov_op))
9111541Srgrimes				(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
9121541Srgrimes			else
9131541Srgrimes#endif
9141541Srgrimes			if (uiop->uio_segflg == UIO_SYSSPACE)
9151541Srgrimes				bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
9161541Srgrimes			else
9171541Srgrimes				copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
9181541Srgrimes			mp->m_len += xfer;
9191541Srgrimes			left -= xfer;
9201541Srgrimes			uiocp += xfer;
9211541Srgrimes			uiop->uio_offset += xfer;
9221541Srgrimes			uiop->uio_resid -= xfer;
9231541Srgrimes		}
92417186Sdfr		uiop->uio_iov->iov_base += uiosiz;
92517186Sdfr		uiop->uio_iov->iov_len -= uiosiz;
9261541Srgrimes		siz -= uiosiz;
9271541Srgrimes	}
9281541Srgrimes	if (rem > 0) {
9291541Srgrimes		if (rem > M_TRAILINGSPACE(mp)) {
9301541Srgrimes			MGET(mp, M_WAIT, MT_DATA);
9311541Srgrimes			mp->m_len = 0;
9321541Srgrimes			mp2->m_next = mp;
9331541Srgrimes		}
9341541Srgrimes		cp = mtod(mp, caddr_t)+mp->m_len;
9351541Srgrimes		for (left = 0; left < rem; left++)
9361541Srgrimes			*cp++ = '\0';
9371541Srgrimes		mp->m_len += rem;
9381541Srgrimes		*bpos = cp;
9391541Srgrimes	} else
9401541Srgrimes		*bpos = mtod(mp, caddr_t)+mp->m_len;
9411541Srgrimes	*mq = mp;
9421541Srgrimes	return (0);
9431541Srgrimes}
9441541Srgrimes
9451541Srgrimes/*
9461541Srgrimes * Help break down an mbuf chain by setting the first siz bytes contiguous
9471541Srgrimes * pointed to by returned val.
9481541Srgrimes * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
9491541Srgrimes * cases. (The macros use the vars. dpos and dpos2)
9501541Srgrimes */
9511549Srgrimesint
9521541Srgrimesnfsm_disct(mdp, dposp, siz, left, cp2)
9531541Srgrimes	struct mbuf **mdp;
9541541Srgrimes	caddr_t *dposp;
9551541Srgrimes	int siz;
9561541Srgrimes	int left;
9571541Srgrimes	caddr_t *cp2;
9581541Srgrimes{
9591541Srgrimes	register struct mbuf *mp, *mp2;
9601541Srgrimes	register int siz2, xfer;
9611541Srgrimes	register caddr_t p;
9621541Srgrimes
9631541Srgrimes	mp = *mdp;
9641541Srgrimes	while (left == 0) {
9651541Srgrimes		*mdp = mp = mp->m_next;
9661541Srgrimes		if (mp == NULL)
9671541Srgrimes			return (EBADRPC);
9681541Srgrimes		left = mp->m_len;
9691541Srgrimes		*dposp = mtod(mp, caddr_t);
9701541Srgrimes	}
9711541Srgrimes	if (left >= siz) {
9721541Srgrimes		*cp2 = *dposp;
9731541Srgrimes		*dposp += siz;
9741541Srgrimes	} else if (mp->m_next == NULL) {
9751541Srgrimes		return (EBADRPC);
9761541Srgrimes	} else if (siz > MHLEN) {
9771541Srgrimes		panic("nfs S too big");
9781541Srgrimes	} else {
9791541Srgrimes		MGET(mp2, M_WAIT, MT_DATA);
9801541Srgrimes		mp2->m_next = mp->m_next;
9811541Srgrimes		mp->m_next = mp2;
9821541Srgrimes		mp->m_len -= left;
9831541Srgrimes		mp = mp2;
9841541Srgrimes		*cp2 = p = mtod(mp, caddr_t);
9851541Srgrimes		bcopy(*dposp, p, left);		/* Copy what was left */
9861541Srgrimes		siz2 = siz-left;
9871541Srgrimes		p += left;
9881541Srgrimes		mp2 = mp->m_next;
9891541Srgrimes		/* Loop around copying up the siz2 bytes */
9901541Srgrimes		while (siz2 > 0) {
9911541Srgrimes			if (mp2 == NULL)
9921541Srgrimes				return (EBADRPC);
9931541Srgrimes			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
9941541Srgrimes			if (xfer > 0) {
9951541Srgrimes				bcopy(mtod(mp2, caddr_t), p, xfer);
9961541Srgrimes				NFSMADV(mp2, xfer);
9971541Srgrimes				mp2->m_len -= xfer;
9981541Srgrimes				p += xfer;
9991541Srgrimes				siz2 -= xfer;
10001541Srgrimes			}
10011541Srgrimes			if (siz2 > 0)
10021541Srgrimes				mp2 = mp2->m_next;
10031541Srgrimes		}
10041541Srgrimes		mp->m_len = siz;
10051541Srgrimes		*mdp = mp2;
10061541Srgrimes		*dposp = mtod(mp2, caddr_t);
10071541Srgrimes	}
10081541Srgrimes	return (0);
10091541Srgrimes}
10101541Srgrimes
10111541Srgrimes/*
10121541Srgrimes * Advance the position in the mbuf chain.
10131541Srgrimes */
10141549Srgrimesint
10151541Srgrimesnfs_adv(mdp, dposp, offs, left)
10161541Srgrimes	struct mbuf **mdp;
10171541Srgrimes	caddr_t *dposp;
10181541Srgrimes	int offs;
10191541Srgrimes	int left;
10201541Srgrimes{
10211541Srgrimes	register struct mbuf *m;
10221541Srgrimes	register int s;
10231541Srgrimes
10241541Srgrimes	m = *mdp;
10251541Srgrimes	s = left;
10261541Srgrimes	while (s < offs) {
10271541Srgrimes		offs -= s;
10281541Srgrimes		m = m->m_next;
10291541Srgrimes		if (m == NULL)
10301541Srgrimes			return (EBADRPC);
10311541Srgrimes		s = m->m_len;
10321541Srgrimes	}
10331541Srgrimes	*mdp = m;
10341541Srgrimes	*dposp = mtod(m, caddr_t)+offs;
10351541Srgrimes	return (0);
10361541Srgrimes}
10371541Srgrimes
10381541Srgrimes/*
10391541Srgrimes * Copy a string into mbufs for the hard cases...
10401541Srgrimes */
10411549Srgrimesint
10421541Srgrimesnfsm_strtmbuf(mb, bpos, cp, siz)
10431541Srgrimes	struct mbuf **mb;
10441541Srgrimes	char **bpos;
104536511Speter	const char *cp;
10461541Srgrimes	long siz;
10471541Srgrimes{
104836519Speter	register struct mbuf *m1 = NULL, *m2;
10491541Srgrimes	long left, xfer, len, tlen;
105036541Speter	u_int32_t *tl;
10511541Srgrimes	int putsize;
10521541Srgrimes
10531541Srgrimes	putsize = 1;
10541541Srgrimes	m2 = *mb;
10551541Srgrimes	left = M_TRAILINGSPACE(m2);
10561541Srgrimes	if (left > 0) {
105736541Speter		tl = ((u_int32_t *)(*bpos));
10581541Srgrimes		*tl++ = txdr_unsigned(siz);
10591541Srgrimes		putsize = 0;
10601541Srgrimes		left -= NFSX_UNSIGNED;
10611541Srgrimes		m2->m_len += NFSX_UNSIGNED;
10621541Srgrimes		if (left > 0) {
10631541Srgrimes			bcopy(cp, (caddr_t) tl, left);
10641541Srgrimes			siz -= left;
10651541Srgrimes			cp += left;
10661541Srgrimes			m2->m_len += left;
10671541Srgrimes			left = 0;
10681541Srgrimes		}
10691541Srgrimes	}
10701541Srgrimes	/* Loop around adding mbufs */
10711541Srgrimes	while (siz > 0) {
10721541Srgrimes		MGET(m1, M_WAIT, MT_DATA);
10731541Srgrimes		if (siz > MLEN)
10741541Srgrimes			MCLGET(m1, M_WAIT);
10751541Srgrimes		m1->m_len = NFSMSIZ(m1);
10761541Srgrimes		m2->m_next = m1;
10771541Srgrimes		m2 = m1;
107836541Speter		tl = mtod(m1, u_int32_t *);
10791541Srgrimes		tlen = 0;
10801541Srgrimes		if (putsize) {
10811541Srgrimes			*tl++ = txdr_unsigned(siz);
10821541Srgrimes			m1->m_len -= NFSX_UNSIGNED;
10831541Srgrimes			tlen = NFSX_UNSIGNED;
10841541Srgrimes			putsize = 0;
10851541Srgrimes		}
10861541Srgrimes		if (siz < m1->m_len) {
10871541Srgrimes			len = nfsm_rndup(siz);
10881541Srgrimes			xfer = siz;
10891541Srgrimes			if (xfer < len)
10901541Srgrimes				*(tl+(xfer>>2)) = 0;
10911541Srgrimes		} else {
10921541Srgrimes			xfer = len = m1->m_len;
10931541Srgrimes		}
10941541Srgrimes		bcopy(cp, (caddr_t) tl, xfer);
10951541Srgrimes		m1->m_len = len+tlen;
10961541Srgrimes		siz -= xfer;
10971541Srgrimes		cp += xfer;
10981541Srgrimes	}
10991541Srgrimes	*mb = m1;
11001541Srgrimes	*bpos = mtod(m1, caddr_t)+m1->m_len;
11011541Srgrimes	return (0);
11021541Srgrimes}
11031541Srgrimes
11041541Srgrimes/*
11051541Srgrimes * Called once to initialize data structures...
11061541Srgrimes */
11071549Srgrimesint
110822521Sdysonnfs_init(vfsp)
110922521Sdyson	struct vfsconf *vfsp;
11101541Srgrimes{
11111541Srgrimes	register int i;
11121541Srgrimes
111336329Speter	nfsmount_zone = zinit("NFSMOUNT", sizeof(struct nfsmount), 0, 0, 1);
111436329Speter
11159336Sdfr	/*
11169336Sdfr	 * Check to see if major data structures haven't bloated.
11179336Sdfr	 */
11189336Sdfr	if (sizeof (struct nfssvc_sock) > NFS_SVCALLOC) {
11199336Sdfr		printf("struct nfssvc_sock bloated (> %dbytes)\n",NFS_SVCALLOC);
11209336Sdfr		printf("Try reducing NFS_UIDHASHSIZ\n");
11219336Sdfr	}
11229336Sdfr	if (sizeof (struct nfsuid) > NFS_UIDALLOC) {
11239336Sdfr		printf("struct nfsuid bloated (> %dbytes)\n",NFS_UIDALLOC);
11249336Sdfr		printf("Try unionizing the nu_nickname and nu_flag fields\n");
11259336Sdfr	}
112622521Sdyson	nfs_mount_type = vfsp->vfc_typenum;
11271541Srgrimes	nfsrtt.pos = 0;
11281541Srgrimes	rpc_vers = txdr_unsigned(RPC_VER2);
11291541Srgrimes	rpc_call = txdr_unsigned(RPC_CALL);
11301541Srgrimes	rpc_reply = txdr_unsigned(RPC_REPLY);
11311541Srgrimes	rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
11321541Srgrimes	rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
11331541Srgrimes	rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
11341541Srgrimes	rpc_autherr = txdr_unsigned(RPC_AUTHERR);
11351541Srgrimes	rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
11369336Sdfr	rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4);
11371541Srgrimes	nfs_prog = txdr_unsigned(NFS_PROG);
11389336Sdfr	nqnfs_prog = txdr_unsigned(NQNFS_PROG);
11391541Srgrimes	nfs_true = txdr_unsigned(TRUE);
11401541Srgrimes	nfs_false = txdr_unsigned(FALSE);
11413664Sphk	nfs_xdrneg1 = txdr_unsigned(-1);
11429336Sdfr	nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
11439336Sdfr	if (nfs_ticks < 1)
11449336Sdfr		nfs_ticks = 1;
11451541Srgrimes	/* Ensure async daemons disabled */
114619449Sdfr	for (i = 0; i < NFS_MAXASYNCDAEMON; i++) {
11471541Srgrimes		nfs_iodwant[i] = (struct proc *)0;
114819449Sdfr		nfs_iodmount[i] = (struct nfsmount *)0;
114919449Sdfr	}
11501541Srgrimes	nfs_nhinit();			/* Init the nfsnode table */
115113416Sphk#ifndef NFS_NOSERVER
11521541Srgrimes	nfsrv_init(0);			/* Init server data structures */
11531541Srgrimes	nfsrv_initcache();		/* Init the server request cache */
115413416Sphk#endif
11551541Srgrimes
11561541Srgrimes	/*
11571541Srgrimes	 * Initialize the nqnfs server stuff.
11581541Srgrimes	 */
11591541Srgrimes	if (nqnfsstarttime == 0) {
11601541Srgrimes		nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
11611541Srgrimes			+ nqsrv_clockskew + nqsrv_writeslack;
11621541Srgrimes		NQLOADNOVRAM(nqnfsstarttime);
11633664Sphk		CIRCLEQ_INIT(&nqtimerhead);
11643664Sphk		nqfhhashtbl = hashinit(NQLCHSZ, M_NQLEASE, &nqfhhash);
11651541Srgrimes	}
11661541Srgrimes
11671541Srgrimes	/*
11681541Srgrimes	 * Initialize reply list and start timer
11691541Srgrimes	 */
11703664Sphk	TAILQ_INIT(&nfs_reqq);
117116365Sphk
11723305Sphk	nfs_timer(0);
11731549Srgrimes
11742997Swollman	/*
11752997Swollman	 * Set up lease_check and lease_updatetime so that other parts
11762997Swollman	 * of the system can call us, if we are loadable.
11772997Swollman	 */
117813416Sphk#ifndef NFS_NOSERVER
117938894Sbde	nfs_prev_vop_lease_check = default_vnodeop_p[VOFFSET(vop_lease)];
118030738Sphk	default_vnodeop_p[VOFFSET(vop_lease)] = (vop_t *)nqnfs_vop_lease_check;
118113416Sphk#endif
118238894Sbde	nfs_prev_lease_updatetime = lease_updatetime;
11832997Swollman	lease_updatetime = nfs_lease_updatetime;
118438894Sbde	nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
11852997Swollman	sysent[SYS_nfssvc].sy_narg = 2;
118638894Sbde	nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
118730738Sphk	sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
118813416Sphk#ifndef NFS_NOSERVER
118938894Sbde	nfs_prev_getfh_sy_narg = sysent[SYS_getfh].sy_narg;
11902997Swollman	sysent[SYS_getfh].sy_narg = 2;
119138894Sbde	nfs_prev_getfh_sy_call = sysent[SYS_getfh].sy_call;
119230738Sphk	sysent[SYS_getfh].sy_call = (sy_call_t *)getfh;
11932997Swollman#endif
11942997Swollman
119542957Sdillon	nfs_pbuf_freecnt = nswbuf / 2 + 1;
119642957Sdillon
11971549Srgrimes	return (0);
11981541Srgrimes}
11991541Srgrimes
120038894Sbdeint
120138894Sbdenfs_uninit(vfsp)
120238894Sbde	struct vfsconf *vfsp;
120338894Sbde{
120438894Sbde
120538894Sbde	untimeout(nfs_timer, (void *)NULL, nfs_timer_handle);
120638894Sbde	nfs_mount_type = -1;
120738894Sbde#ifndef NFS_NOSERVER
120838894Sbde	default_vnodeop_p[VOFFSET(vop_lease)] = nfs_prev_vop_lease_check;
120938894Sbde#endif
121038894Sbde	lease_updatetime = nfs_prev_lease_updatetime;
121138894Sbde	sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
121238894Sbde	sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
121338894Sbde#ifndef NFS_NOSERVER
121438894Sbde	sysent[SYS_getfh].sy_narg = nfs_prev_getfh_sy_narg;
121538894Sbde	sysent[SYS_getfh].sy_call = nfs_prev_getfh_sy_call;
121638894Sbde#endif
121738894Sbde	return (0);
121838894Sbde}
121938894Sbde
12201541Srgrimes/*
12211541Srgrimes * Attribute cache routines.
12221541Srgrimes * nfs_loadattrcache() - loads or updates the cache contents from attributes
12231541Srgrimes *	that are on the mbuf list
12241541Srgrimes * nfs_getattrcache() - returns valid attributes if found in cache, returns
12251541Srgrimes *	error otherwise
12261541Srgrimes */
12271541Srgrimes
12281541Srgrimes/*
12291541Srgrimes * Load the attribute cache (that lives in the nfsnode entry) with
12301541Srgrimes * the values on the mbuf list and
12311541Srgrimes * Iff vap not NULL
12321541Srgrimes *    copy the attributes to *vaper
12331541Srgrimes */
12341549Srgrimesint
12351541Srgrimesnfs_loadattrcache(vpp, mdp, dposp, vaper)
12361541Srgrimes	struct vnode **vpp;
12371541Srgrimes	struct mbuf **mdp;
12381541Srgrimes	caddr_t *dposp;
12391541Srgrimes	struct vattr *vaper;
12401541Srgrimes{
12411541Srgrimes	register struct vnode *vp = *vpp;
12421541Srgrimes	register struct vattr *vap;
12439336Sdfr	register struct nfs_fattr *fp;
12443664Sphk	register struct nfsnode *np;
124536541Speter	register int32_t t1;
12469336Sdfr	caddr_t cp2;
12479336Sdfr	int error = 0, rdev;
12481541Srgrimes	struct mbuf *md;
12491541Srgrimes	enum vtype vtyp;
12501541Srgrimes	u_short vmode;
12511541Srgrimes	struct timespec mtime;
12521541Srgrimes	struct vnode *nvp;
12539336Sdfr	int v3 = NFS_ISV3(vp);
12541541Srgrimes
12551541Srgrimes	md = *mdp;
12569336Sdfr	t1 = (mtod(md, caddr_t) + md->m_len) - *dposp;
125743305Sdillon	if ((error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2)) != 0)
12581541Srgrimes		return (error);
12599336Sdfr	fp = (struct nfs_fattr *)cp2;
12609336Sdfr	if (v3) {
12619336Sdfr		vtyp = nfsv3tov_type(fp->fa_type);
12629336Sdfr		vmode = fxdr_unsigned(u_short, fp->fa_mode);
126347028Sphk		rdev = umakedev(fxdr_unsigned(int, fp->fa3_rdev.specdata1),
126416634Sbde			fxdr_unsigned(int, fp->fa3_rdev.specdata2));
12659336Sdfr		fxdr_nfsv3time(&fp->fa3_mtime, &mtime);
12661541Srgrimes	} else {
12679336Sdfr		vtyp = nfsv2tov_type(fp->fa_type);
12689336Sdfr		vmode = fxdr_unsigned(u_short, fp->fa_mode);
12699336Sdfr		/*
12709336Sdfr		 * XXX
12719336Sdfr		 *
12729336Sdfr		 * The duplicate information returned in fa_type and fa_mode
12739336Sdfr		 * is an ambiguity in the NFS version 2 protocol.
12749336Sdfr		 *
12759336Sdfr		 * VREG should be taken literally as a regular file.  If a
12769336Sdfr		 * server intents to return some type information differently
12779336Sdfr		 * in the upper bits of the mode field (e.g. for sockets, or
12789336Sdfr		 * FIFOs), NFSv2 mandates fa_type to be VNON.  Anyway, we
12799336Sdfr		 * leave the examination of the mode bits even in the VREG
12809336Sdfr		 * case to avoid breakage for bogus servers, but we make sure
12819336Sdfr		 * that there are actually type bits set in the upper part of
12829336Sdfr		 * fa_mode (and failing that, trust the va_type field).
12839336Sdfr		 *
12849336Sdfr		 * NFSv3 cleared the issue, and requires fa_mode to not
12859336Sdfr		 * contain any type information (while also introduing sockets
12869336Sdfr		 * and FIFOs for fa_type).
12879336Sdfr		 */
12889336Sdfr		if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0))
12899336Sdfr			vtyp = IFTOVT(vmode);
129036541Speter		rdev = fxdr_unsigned(int32_t, fp->fa2_rdev);
12919336Sdfr		fxdr_nfsv2time(&fp->fa2_mtime, &mtime);
12929336Sdfr
12939336Sdfr		/*
12949336Sdfr		 * Really ugly NFSv2 kludge.
12959336Sdfr		 */
12969336Sdfr		if (vtyp == VCHR && rdev == 0xffffffff)
12979336Sdfr			vtyp = VFIFO;
12981541Srgrimes	}
12999336Sdfr
13001541Srgrimes	/*
13011541Srgrimes	 * If v_type == VNON it is a new node, so fill in the v_type,
13028876Srgrimes	 * n_mtime fields. Check to see if it represents a special
13031541Srgrimes	 * device, and if so, check for a possible alias. Once the
13041541Srgrimes	 * correct vnode has been obtained, fill in the rest of the
13051541Srgrimes	 * information.
13061541Srgrimes	 */
13071541Srgrimes	np = VTONFS(vp);
130810219Sdfr	if (vp->v_type != vtyp) {
13099336Sdfr		vp->v_type = vtyp;
13101541Srgrimes		if (vp->v_type == VFIFO) {
13111541Srgrimes			vp->v_op = fifo_nfsv2nodeop_p;
13121541Srgrimes		}
13131541Srgrimes		if (vp->v_type == VCHR || vp->v_type == VBLK) {
13141541Srgrimes			vp->v_op = spec_nfsv2nodeop_p;
131547028Sphk			nvp = checkalias(vp, rdev, vp->v_mount);
13163305Sphk			if (nvp) {
13171541Srgrimes				/*
13181541Srgrimes				 * Discard unneeded vnode, but save its nfsnode.
131922521Sdyson				 * Since the nfsnode does not have a lock, its
132022521Sdyson				 * vnode lock has to be carried over.
13211541Srgrimes				 */
132222521Sdyson				nvp->v_vnlock = vp->v_vnlock;
132322521Sdyson				vp->v_vnlock = NULL;
13241541Srgrimes				nvp->v_data = vp->v_data;
13251541Srgrimes				vp->v_data = NULL;
13261541Srgrimes				vp->v_op = spec_vnodeop_p;
13271541Srgrimes				vrele(vp);
13281541Srgrimes				vgone(vp);
13291541Srgrimes				/*
13301541Srgrimes				 * Reinitialize aliased node.
13311541Srgrimes				 */
13321541Srgrimes				np->n_vnode = nvp;
13331541Srgrimes				*vpp = vp = nvp;
13341541Srgrimes			}
13351541Srgrimes		}
133618397Snate		np->n_mtime = mtime.tv_sec;
13371541Srgrimes	}
13381541Srgrimes	vap = &np->n_vattr;
13391541Srgrimes	vap->va_type = vtyp;
13401541Srgrimes	vap->va_mode = (vmode & 07777);
134147028Sphk	vap->va_rdev = rdev;
13421541Srgrimes	vap->va_mtime = mtime;
13431541Srgrimes	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
13449336Sdfr	if (v3) {
13459336Sdfr		vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
13469336Sdfr		vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
13479336Sdfr		vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
134847751Speter		vap->va_size = fxdr_hyper(&fp->fa3_size);
13499336Sdfr		vap->va_blocksize = NFS_FABLKSIZE;
135047751Speter		vap->va_bytes = fxdr_hyper(&fp->fa3_used);
135136541Speter		vap->va_fileid = fxdr_unsigned(int32_t,
135236541Speter		    fp->fa3_fileid.nfsuquad[1]);
13539336Sdfr		fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime);
13549336Sdfr		fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime);
13559336Sdfr		vap->va_flags = 0;
13569336Sdfr		vap->va_filerev = 0;
13571541Srgrimes	} else {
13589336Sdfr		vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
13599336Sdfr		vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
13609336Sdfr		vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
136136541Speter		vap->va_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
136236541Speter		vap->va_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
136347751Speter		vap->va_bytes = (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks)
136436541Speter		    * NFS_FABLKSIZE;
136536541Speter		vap->va_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
13669336Sdfr		fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime);
13671541Srgrimes		vap->va_flags = 0;
136836541Speter		vap->va_ctime.tv_sec = fxdr_unsigned(u_int32_t,
136936541Speter		    fp->fa2_ctime.nfsv2_sec);
137018397Snate		vap->va_ctime.tv_nsec = 0;
137136541Speter		vap->va_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
13721541Srgrimes		vap->va_filerev = 0;
13731541Srgrimes	}
13741541Srgrimes	if (vap->va_size != np->n_size) {
13751541Srgrimes		if (vap->va_type == VREG) {
13761541Srgrimes			if (np->n_flag & NMODIFIED) {
13771541Srgrimes				if (vap->va_size < np->n_size)
13781541Srgrimes					vap->va_size = np->n_size;
13791541Srgrimes				else
13801541Srgrimes					np->n_size = vap->va_size;
13811541Srgrimes			} else
13821541Srgrimes				np->n_size = vap->va_size;
138341026Speter			vnode_pager_setsize(vp, np->n_size);
13841541Srgrimes		} else
13851541Srgrimes			np->n_size = vap->va_size;
13861541Srgrimes	}
138734961Sphk	np->n_attrstamp = time_second;
13881541Srgrimes	if (vaper != NULL) {
13891541Srgrimes		bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
13901541Srgrimes		if (np->n_flag & NCHG) {
13919336Sdfr			if (np->n_flag & NACC)
13929336Sdfr				vaper->va_atime = np->n_atim;
13939336Sdfr			if (np->n_flag & NUPD)
13949336Sdfr				vaper->va_mtime = np->n_mtim;
13951541Srgrimes		}
13961541Srgrimes	}
13971541Srgrimes	return (0);
13981541Srgrimes}
13991541Srgrimes
140036176Speter#ifdef NFS_ACDEBUG
140136176Speter#include <sys/sysctl.h>
140244101SbdeSYSCTL_DECL(_vfs_nfs);
140336176Speterstatic int nfs_acdebug;
140436176SpeterSYSCTL_INT(_vfs_nfs, OID_AUTO, acdebug, CTLFLAG_RW, &nfs_acdebug, 0, "");
140536176Speter#endif
140636176Speter
14071541Srgrimes/*
14081541Srgrimes * Check the time stamp
14091541Srgrimes * If the cache is valid, copy contents to *vap and return 0
14101541Srgrimes * otherwise return an error
14111541Srgrimes */
14121549Srgrimesint
14131541Srgrimesnfs_getattrcache(vp, vaper)
14141541Srgrimes	register struct vnode *vp;
14151541Srgrimes	struct vattr *vaper;
14161541Srgrimes{
141736176Speter	register struct nfsnode *np;
14181541Srgrimes	register struct vattr *vap;
141936176Speter	struct nfsmount *nmp;
142036176Speter	int timeo;
14211541Srgrimes
142236176Speter	np = VTONFS(vp);
142336176Speter	vap = &np->n_vattr;
142436176Speter	nmp = VFSTONFS(vp->v_mount);
142536176Speter	/* XXX n_mtime doesn't seem to be updated on a miss-and-reload */
142636176Speter	timeo = (time_second - np->n_mtime) / 10;
142736176Speter
142836176Speter#ifdef NFS_ACDEBUG
142936176Speter	if (nfs_acdebug>1)
143036176Speter		printf("nfs_getattrcache: initial timeo = %d\n", timeo);
143136176Speter#endif
143236176Speter
143336176Speter	if (vap->va_type == VDIR) {
143436176Speter		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acdirmin)
143536176Speter			timeo = nmp->nm_acdirmin;
143636176Speter		else if (timeo > nmp->nm_acdirmax)
143736176Speter			timeo = nmp->nm_acdirmax;
143836176Speter	} else {
143936176Speter		if ((np->n_flag & NMODIFIED) || timeo < nmp->nm_acregmin)
144036176Speter			timeo = nmp->nm_acregmin;
144136176Speter		else if (timeo > nmp->nm_acregmax)
144236176Speter			timeo = nmp->nm_acregmax;
144336176Speter	}
144436176Speter
144536176Speter#ifdef NFS_ACDEBUG
144636176Speter	if (nfs_acdebug > 2)
144736176Speter		printf("acregmin %d; acregmax %d; acdirmin %d; acdirmax %d\n",
144836176Speter			nmp->nm_acregmin, nmp->nm_acregmax,
144936176Speter			nmp->nm_acdirmin, nmp->nm_acdirmax);
145036176Speter
145136176Speter	if (nfs_acdebug)
145237089Sbde		printf("nfs_getattrcache: age = %d; final timeo = %d\n",
145336176Speter			(time_second - np->n_attrstamp), timeo);
145436176Speter#endif
145536176Speter
145636176Speter	if ((time_second - np->n_attrstamp) >= timeo) {
14571541Srgrimes		nfsstats.attrcache_misses++;
14581541Srgrimes		return (ENOENT);
14591541Srgrimes	}
14601541Srgrimes	nfsstats.attrcache_hits++;
14611541Srgrimes	if (vap->va_size != np->n_size) {
14621541Srgrimes		if (vap->va_type == VREG) {
14631541Srgrimes			if (np->n_flag & NMODIFIED) {
14641541Srgrimes				if (vap->va_size < np->n_size)
14651541Srgrimes					vap->va_size = np->n_size;
14661541Srgrimes				else
14671541Srgrimes					np->n_size = vap->va_size;
14681541Srgrimes			} else
14691541Srgrimes				np->n_size = vap->va_size;
147041026Speter			vnode_pager_setsize(vp, np->n_size);
14711541Srgrimes		} else
14721541Srgrimes			np->n_size = vap->va_size;
14731541Srgrimes	}
14741541Srgrimes	bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
14751541Srgrimes	if (np->n_flag & NCHG) {
14769336Sdfr		if (np->n_flag & NACC)
14779336Sdfr			vaper->va_atime = np->n_atim;
14789336Sdfr		if (np->n_flag & NUPD)
14799336Sdfr			vaper->va_mtime = np->n_mtim;
14801541Srgrimes	}
14811541Srgrimes	return (0);
14821541Srgrimes}
14831541Srgrimes
148413416Sphk#ifndef NFS_NOSERVER
14851541Srgrimes/*
148627446Sdfr * Set up nameidata for a lookup() call and do it.
148727446Sdfr *
148827446Sdfr * If pubflag is set, this call is done for a lookup operation on the
148927446Sdfr * public filehandle. In that case we allow crossing mountpoints and
149027446Sdfr * absolute pathnames. However, the caller is expected to check that
149127446Sdfr * the lookup result is within the public fs, and deny access if
149227446Sdfr * it is not.
149348125Sjulian *
149448125Sjulian * nfs_namei() clears out garbage fields that namei() might leave garbage.
149548125Sjulian * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
149648125Sjulian * error occurs but the parent was not requested.
149748125Sjulian *
149848125Sjulian * dirp may be set whether an error is returned or not, and must be
149948125Sjulian * released by the caller.
15001541Srgrimes */
15011549Srgrimesint
150227446Sdfrnfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag, pubflag)
15031541Srgrimes	register struct nameidata *ndp;
15041541Srgrimes	fhandle_t *fhp;
15051541Srgrimes	int len;
15061541Srgrimes	struct nfssvc_sock *slp;
150728270Swollman	struct sockaddr *nam;
15081541Srgrimes	struct mbuf **mdp;
15091541Srgrimes	caddr_t *dposp;
15109336Sdfr	struct vnode **retdirp;
15111541Srgrimes	struct proc *p;
151227446Sdfr	int kerbflag, pubflag;
15131541Srgrimes{
15141541Srgrimes	register int i, rem;
15151541Srgrimes	register struct mbuf *md;
151627446Sdfr	register char *fromcp, *tocp, *cp;
151727446Sdfr	struct iovec aiov;
151827446Sdfr	struct uio auio;
15191541Srgrimes	struct vnode *dp;
152027446Sdfr	int error, rdonly, linklen;
15211541Srgrimes	struct componentname *cnp = &ndp->ni_cnd;
15221541Srgrimes
15239336Sdfr	*retdirp = (struct vnode *)0;
152429653Sdyson	cnp->cn_pnbuf = zalloc(namei_zone);
152529653Sdyson
15261541Srgrimes	/*
15271541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
15281541Srgrimes	 * and set the various ndp fields appropriately.
15291541Srgrimes	 */
15301541Srgrimes	fromcp = *dposp;
15311541Srgrimes	tocp = cnp->cn_pnbuf;
15321541Srgrimes	md = *mdp;
15331541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
15341541Srgrimes	cnp->cn_hash = 0;
15351541Srgrimes	for (i = 0; i < len; i++) {
15361541Srgrimes		while (rem == 0) {
15371541Srgrimes			md = md->m_next;
15381541Srgrimes			if (md == NULL) {
15391541Srgrimes				error = EBADRPC;
15401541Srgrimes				goto out;
15411541Srgrimes			}
15421541Srgrimes			fromcp = mtod(md, caddr_t);
15431541Srgrimes			rem = md->m_len;
15441541Srgrimes		}
154527446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
15469336Sdfr			error = EACCES;
15471541Srgrimes			goto out;
15481541Srgrimes		}
15491541Srgrimes		cnp->cn_hash += (unsigned char)*fromcp;
15501541Srgrimes		*tocp++ = *fromcp++;
15511541Srgrimes		rem--;
15521541Srgrimes	}
15531541Srgrimes	*tocp = '\0';
15541541Srgrimes	*mdp = md;
15551541Srgrimes	*dposp = fromcp;
15561541Srgrimes	len = nfsm_rndup(len)-len;
15571541Srgrimes	if (len > 0) {
15581541Srgrimes		if (rem >= len)
15591541Srgrimes			*dposp += len;
156027609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
15619336Sdfr			goto out;
15621541Srgrimes	}
156327446Sdfr
15641541Srgrimes	/*
15651541Srgrimes	 * Extract and set starting directory.
15661541Srgrimes	 */
156727446Sdfr	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
156827446Sdfr	    nam, &rdonly, kerbflag, pubflag);
156927446Sdfr	if (error)
15701541Srgrimes		goto out;
15711541Srgrimes	if (dp->v_type != VDIR) {
157217761Sdyson		vrele(dp);
15731541Srgrimes		error = ENOTDIR;
15741541Srgrimes		goto out;
15751541Srgrimes	}
157627446Sdfr
157727446Sdfr	if (rdonly)
157827446Sdfr		cnp->cn_flags |= RDONLY;
157927446Sdfr
158048125Sjulian	/*
158148125Sjulian	 * Set return directory.  Reference to dp is implicitly transfered
158248125Sjulian	 * to the returned pointer
158348125Sjulian	 */
158427609Sdfr	*retdirp = dp;
158527609Sdfr
158627446Sdfr	if (pubflag) {
158727446Sdfr		/*
158827446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
158927446Sdfr		 * and the 'native path' indicator.
159027446Sdfr		 */
159129653Sdyson		cp = zalloc(namei_zone);
159227446Sdfr		fromcp = cnp->cn_pnbuf;
159327446Sdfr		tocp = cp;
159427446Sdfr		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
159527446Sdfr			switch ((unsigned char)*fromcp) {
159627446Sdfr			case WEBNFS_NATIVE_CHAR:
159727446Sdfr				/*
159827446Sdfr				 * 'Native' path for us is the same
159927446Sdfr				 * as a path according to the NFS spec,
160027446Sdfr				 * just skip the escape char.
160127446Sdfr				 */
160227446Sdfr				fromcp++;
160327446Sdfr				break;
160427446Sdfr			/*
160527446Sdfr			 * More may be added in the future, range 0x80-0xff
160627446Sdfr			 */
160727446Sdfr			default:
160827446Sdfr				error = EIO;
160929653Sdyson				zfree(namei_zone, cp);
161027446Sdfr				goto out;
161127446Sdfr			}
161227446Sdfr		}
161327446Sdfr		/*
161427446Sdfr		 * Translate the '%' escapes, URL-style.
161527446Sdfr		 */
161627446Sdfr		while (*fromcp != '\0') {
161727446Sdfr			if (*fromcp == WEBNFS_ESC_CHAR) {
161827446Sdfr				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
161927446Sdfr					fromcp++;
162027446Sdfr					*tocp++ = HEXSTRTOI(fromcp);
162127446Sdfr					fromcp += 2;
162227446Sdfr					continue;
162327446Sdfr				} else {
162427446Sdfr					error = ENOENT;
162529653Sdyson					zfree(namei_zone, cp);
162627446Sdfr					goto out;
162727446Sdfr				}
162827446Sdfr			} else
162927446Sdfr				*tocp++ = *fromcp++;
163027446Sdfr		}
163127446Sdfr		*tocp = '\0';
163229653Sdyson		zfree(namei_zone, cnp->cn_pnbuf);
163327446Sdfr		cnp->cn_pnbuf = cp;
163427446Sdfr	}
163527446Sdfr
163627446Sdfr	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
163727446Sdfr	ndp->ni_segflg = UIO_SYSSPACE;
163827446Sdfr
163927446Sdfr	if (pubflag) {
164027446Sdfr		ndp->ni_rootdir = rootvnode;
164127446Sdfr		ndp->ni_loopcnt = 0;
164227446Sdfr		if (cnp->cn_pnbuf[0] == '/')
164327446Sdfr			dp = rootvnode;
164427446Sdfr	} else {
164527609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
164627446Sdfr	}
164727446Sdfr
164848125Sjulian	/*
164948125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
165048125Sjulian	 * becuase lookup() will dereference ni_startdir.
165148125Sjulian	 */
165248125Sjulian
165327446Sdfr	cnp->cn_proc = p;
16549336Sdfr	VREF(dp);
165548125Sjulian	ndp->ni_startdir = dp;
165627446Sdfr
165748125Sjulian	for (;;) {
165848125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
165948125Sjulian		/*
166048125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
166148125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
166248125Sjulian		 * we do not have to dereference anything before returning.
166348125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
166448125Sjulian		 * out.
166548125Sjulian		 */
166648125Sjulian		error = lookup(ndp);
166748125Sjulian		if (error)
166848125Sjulian			break;
166948125Sjulian
167048125Sjulian		/*
167148125Sjulian		 * Check for encountering a symbolic link.  Trivial
167248125Sjulian		 * termination occurs if no symlink encountered.
167348125Sjulian		 * Note: zfree is safe because error is 0, so we will
167448125Sjulian		 * not zfree it again when we break.
167548125Sjulian		 */
167648125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
167748125Sjulian			nfsrv_object_create(ndp->ni_vp);
167848125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
167948125Sjulian				cnp->cn_flags |= HASBUF;
168048125Sjulian			else
168148125Sjulian				zfree(namei_zone, cnp->cn_pnbuf);
168248125Sjulian			break;
168327446Sdfr		}
168448125Sjulian
168548125Sjulian		/*
168648125Sjulian		 * Validate symlink
168748125Sjulian		 */
16881541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
168927446Sdfr			VOP_UNLOCK(ndp->ni_dvp, 0, p);
169027446Sdfr		if (!pubflag) {
169127446Sdfr			error = EINVAL;
169248125Sjulian			goto badlink2;
169327446Sdfr		}
169427446Sdfr
169527446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
169627446Sdfr			error = ELOOP;
169748125Sjulian			goto badlink2;
169827446Sdfr		}
169927609Sdfr		if (ndp->ni_pathlen > 1)
170029653Sdyson			cp = zalloc(namei_zone);
17011541Srgrimes		else
170227446Sdfr			cp = cnp->cn_pnbuf;
170327446Sdfr		aiov.iov_base = cp;
170427446Sdfr		aiov.iov_len = MAXPATHLEN;
170527446Sdfr		auio.uio_iov = &aiov;
170627446Sdfr		auio.uio_iovcnt = 1;
170727446Sdfr		auio.uio_offset = 0;
170827446Sdfr		auio.uio_rw = UIO_READ;
170927446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
171027446Sdfr		auio.uio_procp = (struct proc *)0;
171127446Sdfr		auio.uio_resid = MAXPATHLEN;
171227446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
171327446Sdfr		if (error) {
171448125Sjulian		badlink1:
171527446Sdfr			if (ndp->ni_pathlen > 1)
171629653Sdyson				zfree(namei_zone, cp);
171748125Sjulian		badlink2:
171848125Sjulian			vrele(ndp->ni_dvp);
171948125Sjulian			vput(ndp->ni_vp);
172027446Sdfr			break;
172127446Sdfr		}
172227446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
172327446Sdfr		if (linklen == 0) {
172427446Sdfr			error = ENOENT;
172548125Sjulian			goto badlink1;
172627446Sdfr		}
172727446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
172827446Sdfr			error = ENAMETOOLONG;
172948125Sjulian			goto badlink1;
173027446Sdfr		}
173148125Sjulian
173248125Sjulian		/*
173348125Sjulian		 * Adjust or replace path
173448125Sjulian		 */
173527446Sdfr		if (ndp->ni_pathlen > 1) {
173627446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
173729653Sdyson			zfree(namei_zone, cnp->cn_pnbuf);
173827446Sdfr			cnp->cn_pnbuf = cp;
173927446Sdfr		} else
174027446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
174127446Sdfr		ndp->ni_pathlen += linklen;
174248125Sjulian
174327446Sdfr		/*
174448125Sjulian		 * Cleanup refs for next loop and check if root directory
174548125Sjulian		 * should replace current directory.  Normally ni_dvp
174648125Sjulian		 * becomes the new base directory and is cleaned up when
174748125Sjulian		 * we loop.  Explicitly null pointers after invalidation
174848125Sjulian		 * to clarify operation.
174927446Sdfr		 */
175048125Sjulian		vput(ndp->ni_vp);
175148125Sjulian		ndp->ni_vp = NULL;
175248125Sjulian
175327446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
175448125Sjulian			vrele(ndp->ni_dvp);
175548125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
175648125Sjulian			VREF(ndp->ni_dvp);
175727446Sdfr		}
175848125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
175948125Sjulian		ndp->ni_dvp = NULL;
17601541Srgrimes	}
176148125Sjulian
176248125Sjulian	/*
176348125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
176448125Sjulian	 * whether an error occurs or not.  This allows the caller to track
176548125Sjulian	 * cleanup state trivially.
176648125Sjulian	 */
17671541Srgrimesout:
176848125Sjulian	if (error) {
176948125Sjulian		zfree(namei_zone, cnp->cn_pnbuf);
177048125Sjulian		ndp->ni_vp = NULL;
177148125Sjulian		ndp->ni_dvp = NULL;
177248125Sjulian		ndp->ni_startdir = NULL;
177348125Sjulian		cnp->cn_flags &= ~HASBUF;
177448125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
177548125Sjulian		ndp->ni_dvp = NULL;
177648125Sjulian	}
17771541Srgrimes	return (error);
17781541Srgrimes}
17791541Srgrimes
17801541Srgrimes/*
17811541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
17821541Srgrimes * boundary and only trims off the back end
17831541Srgrimes */
17841541Srgrimesvoid
17851541Srgrimesnfsm_adj(mp, len, nul)
17861541Srgrimes	struct mbuf *mp;
17871541Srgrimes	register int len;
17881541Srgrimes	int nul;
17891541Srgrimes{
17901541Srgrimes	register struct mbuf *m;
17911541Srgrimes	register int count, i;
17921541Srgrimes	register char *cp;
17931541Srgrimes
17941541Srgrimes	/*
17951541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
17961541Srgrimes	 * calculating its length and finding the last mbuf.
17971541Srgrimes	 * If the adjustment only affects this mbuf, then just
17981541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
17991541Srgrimes	 * after the remaining size.
18001541Srgrimes	 */
18011541Srgrimes	count = 0;
18021541Srgrimes	m = mp;
18031541Srgrimes	for (;;) {
18041541Srgrimes		count += m->m_len;
18051541Srgrimes		if (m->m_next == (struct mbuf *)0)
18061541Srgrimes			break;
18071541Srgrimes		m = m->m_next;
18081541Srgrimes	}
18091541Srgrimes	if (m->m_len > len) {
18101541Srgrimes		m->m_len -= len;
18111541Srgrimes		if (nul > 0) {
18121541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
18131541Srgrimes			for (i = 0; i < nul; i++)
18141541Srgrimes				*cp++ = '\0';
18151541Srgrimes		}
18161541Srgrimes		return;
18171541Srgrimes	}
18181541Srgrimes	count -= len;
18191541Srgrimes	if (count < 0)
18201541Srgrimes		count = 0;
18211541Srgrimes	/*
18221541Srgrimes	 * Correct length for chain is "count".
18231541Srgrimes	 * Find the mbuf with last data, adjust its length,
18241541Srgrimes	 * and toss data from remaining mbufs on chain.
18251541Srgrimes	 */
18261541Srgrimes	for (m = mp; m; m = m->m_next) {
18271541Srgrimes		if (m->m_len >= count) {
18281541Srgrimes			m->m_len = count;
18291541Srgrimes			if (nul > 0) {
18301541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
18311541Srgrimes				for (i = 0; i < nul; i++)
18321541Srgrimes					*cp++ = '\0';
18331541Srgrimes			}
18341541Srgrimes			break;
18351541Srgrimes		}
18361541Srgrimes		count -= m->m_len;
18371541Srgrimes	}
18383305Sphk	for (m = m->m_next;m;m = m->m_next)
18391541Srgrimes		m->m_len = 0;
18401541Srgrimes}
18411541Srgrimes
18421541Srgrimes/*
18439336Sdfr * Make these functions instead of macros, so that the kernel text size
18449336Sdfr * doesn't get too big...
18459336Sdfr */
18469336Sdfrvoid
18479336Sdfrnfsm_srvwcc(nfsd, before_ret, before_vap, after_ret, after_vap, mbp, bposp)
18489336Sdfr	struct nfsrv_descript *nfsd;
18499336Sdfr	int before_ret;
18509336Sdfr	register struct vattr *before_vap;
18519336Sdfr	int after_ret;
18529336Sdfr	struct vattr *after_vap;
18539336Sdfr	struct mbuf **mbp;
18549336Sdfr	char **bposp;
18559336Sdfr{
18569336Sdfr	register struct mbuf *mb = *mbp, *mb2;
18579336Sdfr	register char *bpos = *bposp;
185836541Speter	register u_int32_t *tl;
18599336Sdfr
18609336Sdfr	if (before_ret) {
186136541Speter		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
18629336Sdfr		*tl = nfs_false;
18639336Sdfr	} else {
186436541Speter		nfsm_build(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
18659336Sdfr		*tl++ = nfs_true;
186647751Speter		txdr_hyper(before_vap->va_size, tl);
18679336Sdfr		tl += 2;
18689336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
18699336Sdfr		tl += 2;
18709336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
18719336Sdfr	}
18729336Sdfr	*bposp = bpos;
18739336Sdfr	*mbp = mb;
18749336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
18759336Sdfr}
18769336Sdfr
18779336Sdfrvoid
18789336Sdfrnfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp)
18799336Sdfr	struct nfsrv_descript *nfsd;
18809336Sdfr	int after_ret;
18819336Sdfr	struct vattr *after_vap;
18829336Sdfr	struct mbuf **mbp;
18839336Sdfr	char **bposp;
18849336Sdfr{
18859336Sdfr	register struct mbuf *mb = *mbp, *mb2;
18869336Sdfr	register char *bpos = *bposp;
188736541Speter	register u_int32_t *tl;
18889336Sdfr	register struct nfs_fattr *fp;
18899336Sdfr
18909336Sdfr	if (after_ret) {
189136541Speter		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
18929336Sdfr		*tl = nfs_false;
18939336Sdfr	} else {
189436541Speter		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
18959336Sdfr		*tl++ = nfs_true;
18969336Sdfr		fp = (struct nfs_fattr *)tl;
18979336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
18989336Sdfr	}
18999336Sdfr	*mbp = mb;
19009336Sdfr	*bposp = bpos;
19019336Sdfr}
19029336Sdfr
19039336Sdfrvoid
19049336Sdfrnfsm_srvfattr(nfsd, vap, fp)
19059336Sdfr	register struct nfsrv_descript *nfsd;
19069336Sdfr	register struct vattr *vap;
19079336Sdfr	register struct nfs_fattr *fp;
19089336Sdfr{
19099336Sdfr
19109336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
19119336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
19129336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
19139336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
19149336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
19159336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
191647751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
191747751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
191847028Sphk		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
191947028Sphk		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
19209336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
19219336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
19229336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
19239336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
19249336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
19259336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
19269336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
19279336Sdfr	} else {
19289336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
19299336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
19309336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
19319336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
19329336Sdfr		if (vap->va_type == VFIFO)
19339336Sdfr			fp->fa2_rdev = 0xffffffff;
19349336Sdfr		else
19359336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
19369336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
19379336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
19389336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
19399336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
19409336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
19419336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
19429336Sdfr	}
19439336Sdfr}
19449336Sdfr
19459336Sdfr/*
19461541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
19471541Srgrimes * 	- look up fsid in mount list (if not found ret error)
19481541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
19491541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
19501541Srgrimes *	- if not lockflag unlock it with VOP_UNLOCK()
19511541Srgrimes */
19521549Srgrimesint
195327446Sdfrnfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag, pubflag)
19541541Srgrimes	fhandle_t *fhp;
19551541Srgrimes	int lockflag;
19561541Srgrimes	struct vnode **vpp;
19571541Srgrimes	struct ucred *cred;
19581541Srgrimes	struct nfssvc_sock *slp;
195928270Swollman	struct sockaddr *nam;
19601541Srgrimes	int *rdonlyp;
19619336Sdfr	int kerbflag;
196227446Sdfr	int pubflag;
19631541Srgrimes{
196422521Sdyson	struct proc *p = curproc; /* XXX */
19651541Srgrimes	register struct mount *mp;
19661541Srgrimes	register int i;
19671541Srgrimes	struct ucred *credanon;
19681541Srgrimes	int error, exflags;
196936534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
197036534Speter	struct sockaddr_int *saddr;
197136534Speter#endif
19721541Srgrimes
19731541Srgrimes	*vpp = (struct vnode *)0;
197427446Sdfr
197527446Sdfr	if (nfs_ispublicfh(fhp)) {
197627446Sdfr		if (!pubflag || !nfs_pub.np_valid)
197727446Sdfr			return (ESTALE);
197827446Sdfr		fhp = &nfs_pub.np_handle;
197927446Sdfr	}
198027446Sdfr
198122521Sdyson	mp = vfs_getvfs(&fhp->fh_fsid);
19823305Sphk	if (!mp)
19831541Srgrimes		return (ESTALE);
19843305Sphk	error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon);
19853305Sphk	if (error)
19861541Srgrimes		return (error);
198736534Speter#ifdef MNT_EXNORESPORT
198836534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
198936534Speter		saddr = (struct sockaddr_in *)nam;
199036534Speter		if (saddr->sin_family == AF_INET &&
199136534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
199236534Speter			vput(*vpp);
199336534Speter			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
199436534Speter		}
199536534Speter	}
199636534Speter#endif
19971541Srgrimes	/*
19981541Srgrimes	 * Check/setup credentials.
19991541Srgrimes	 */
20001541Srgrimes	if (exflags & MNT_EXKERB) {
20019336Sdfr		if (!kerbflag) {
20021541Srgrimes			vput(*vpp);
20039336Sdfr			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
20041541Srgrimes		}
20059336Sdfr	} else if (kerbflag) {
20069336Sdfr		vput(*vpp);
20079336Sdfr		return (NFSERR_AUTHERR | AUTH_TOOWEAK);
20081541Srgrimes	} else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
20091541Srgrimes		cred->cr_uid = credanon->cr_uid;
20101541Srgrimes		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
20111541Srgrimes			cred->cr_groups[i] = credanon->cr_groups[i];
20123664Sphk		cred->cr_ngroups = i;
20131541Srgrimes	}
20141541Srgrimes	if (exflags & MNT_EXRDONLY)
20151541Srgrimes		*rdonlyp = 1;
20161541Srgrimes	else
20171541Srgrimes		*rdonlyp = 0;
20187969Sdyson
201917761Sdyson	nfsrv_object_create(*vpp);
20207969Sdyson
20211541Srgrimes	if (!lockflag)
202222521Sdyson		VOP_UNLOCK(*vpp, 0, p);
20231541Srgrimes	return (0);
20241541Srgrimes}
20251541Srgrimes
202627446Sdfr
202727446Sdfr/*
202827446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
202927446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
203027446Sdfr * transformed this to all zeroes in both cases, so check for it.
203127446Sdfr */
203227446Sdfrint
203327446Sdfrnfs_ispublicfh(fhp)
203427446Sdfr	fhandle_t *fhp;
203527446Sdfr{
203627446Sdfr	char *cp = (char *)fhp;
203727446Sdfr	int i;
203827446Sdfr
203927446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
204027446Sdfr		if (*cp++ != 0)
204127446Sdfr			return (FALSE);
204227446Sdfr	return (TRUE);
204327446Sdfr}
204427446Sdfr
204513416Sphk#endif /* NFS_NOSERVER */
20461541Srgrimes/*
20471541Srgrimes * This function compares two net addresses by family and returns TRUE
20481541Srgrimes * if they are the same host.
20491541Srgrimes * If there is any doubt, return FALSE.
20501541Srgrimes * The AF_INET family is handled as a special case so that address mbufs
20511541Srgrimes * don't need to be saved to store "struct in_addr", which is only 4 bytes.
20521541Srgrimes */
20531549Srgrimesint
20541541Srgrimesnetaddr_match(family, haddr, nam)
20551541Srgrimes	int family;
20561541Srgrimes	union nethostaddr *haddr;
205728270Swollman	struct sockaddr *nam;
20581541Srgrimes{
20591541Srgrimes	register struct sockaddr_in *inetaddr;
20601541Srgrimes
20611541Srgrimes	switch (family) {
20621541Srgrimes	case AF_INET:
206328270Swollman		inetaddr = (struct sockaddr_in *)nam;
20641541Srgrimes		if (inetaddr->sin_family == AF_INET &&
20651541Srgrimes		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
20661541Srgrimes			return (1);
20671541Srgrimes		break;
20681541Srgrimes#ifdef ISO
20691541Srgrimes	case AF_ISO:
20701541Srgrimes	    {
20711541Srgrimes		register struct sockaddr_iso *isoaddr1, *isoaddr2;
20721541Srgrimes
207328270Swollman		isoaddr1 = (struct sockaddr_iso *)nam;
207428270Swollman		isoaddr2 = (struct sockaddr_iso *)haddr->had_nam;
20751541Srgrimes		if (isoaddr1->siso_family == AF_ISO &&
20761541Srgrimes		    isoaddr1->siso_nlen > 0 &&
20771541Srgrimes		    isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
20781541Srgrimes		    SAME_ISOADDR(isoaddr1, isoaddr2))
20791541Srgrimes			return (1);
20801541Srgrimes		break;
20811541Srgrimes	    }
20821541Srgrimes#endif	/* ISO */
20831541Srgrimes	default:
20841541Srgrimes		break;
20851541Srgrimes	};
20861541Srgrimes	return (0);
20871541Srgrimes}
20885455Sdg
208943305Sdillonstatic nfsuint64 nfs_nullcookie = { { 0, 0 } };
20909336Sdfr/*
20919336Sdfr * This function finds the directory cookie that corresponds to the
20929336Sdfr * logical byte offset given.
20939336Sdfr */
20949336Sdfrnfsuint64 *
20959336Sdfrnfs_getcookie(np, off, add)
20969336Sdfr	register struct nfsnode *np;
20979336Sdfr	off_t off;
20989336Sdfr	int add;
20999336Sdfr{
21009336Sdfr	register struct nfsdmap *dp, *dp2;
21019336Sdfr	register int pos;
21029336Sdfr
210336979Sbde	pos = (uoff_t)off / NFS_DIRBLKSIZ;
210436979Sbde	if (pos == 0 || off < 0) {
21059336Sdfr#ifdef DIAGNOSTIC
21069336Sdfr		if (add)
210736979Sbde			panic("nfs getcookie add at <= 0");
21089336Sdfr#endif
21099336Sdfr		return (&nfs_nullcookie);
21109336Sdfr	}
21119336Sdfr	pos--;
21129336Sdfr	dp = np->n_cookies.lh_first;
21139336Sdfr	if (!dp) {
21149336Sdfr		if (add) {
21159336Sdfr			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
21169336Sdfr				M_NFSDIROFF, M_WAITOK);
21179336Sdfr			dp->ndm_eocookie = 0;
21189336Sdfr			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
21199336Sdfr		} else
21209336Sdfr			return ((nfsuint64 *)0);
21219336Sdfr	}
21229336Sdfr	while (pos >= NFSNUMCOOKIES) {
21239336Sdfr		pos -= NFSNUMCOOKIES;
21249336Sdfr		if (dp->ndm_list.le_next) {
21259336Sdfr			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
21269336Sdfr				pos >= dp->ndm_eocookie)
21279336Sdfr				return ((nfsuint64 *)0);
21289336Sdfr			dp = dp->ndm_list.le_next;
21299336Sdfr		} else if (add) {
21309336Sdfr			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
21319336Sdfr				M_NFSDIROFF, M_WAITOK);
21329336Sdfr			dp2->ndm_eocookie = 0;
21339336Sdfr			LIST_INSERT_AFTER(dp, dp2, ndm_list);
21349336Sdfr			dp = dp2;
21359336Sdfr		} else
21369336Sdfr			return ((nfsuint64 *)0);
21379336Sdfr	}
21389336Sdfr	if (pos >= dp->ndm_eocookie) {
21399336Sdfr		if (add)
21409336Sdfr			dp->ndm_eocookie = pos + 1;
21419336Sdfr		else
21429336Sdfr			return ((nfsuint64 *)0);
21439336Sdfr	}
21449336Sdfr	return (&dp->ndm_cookies[pos]);
21459336Sdfr}
21469336Sdfr
21479336Sdfr/*
21489336Sdfr * Invalidate cached directory information, except for the actual directory
21499336Sdfr * blocks (which are invalidated separately).
21509336Sdfr * Done mainly to avoid the use of stale offset cookies.
21519336Sdfr */
21529336Sdfrvoid
21539336Sdfrnfs_invaldir(vp)
21549336Sdfr	register struct vnode *vp;
21559336Sdfr{
21569336Sdfr	register struct nfsnode *np = VTONFS(vp);
21579336Sdfr
21589336Sdfr#ifdef DIAGNOSTIC
21599336Sdfr	if (vp->v_type != VDIR)
21609336Sdfr		panic("nfs: invaldir not dir");
21619336Sdfr#endif
21629336Sdfr	np->n_direofoffset = 0;
21639336Sdfr	np->n_cookieverf.nfsuquad[0] = 0;
21649336Sdfr	np->n_cookieverf.nfsuquad[1] = 0;
21659336Sdfr	if (np->n_cookies.lh_first)
21669336Sdfr		np->n_cookies.lh_first->ndm_eocookie = 0;
21679336Sdfr}
21689336Sdfr
21699336Sdfr/*
21709336Sdfr * The write verifier has changed (probably due to a server reboot), so all
21719336Sdfr * B_NEEDCOMMIT blocks will have to be written again. Since they are on the
21729336Sdfr * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT
21739336Sdfr * flag. Once done the new write verifier can be set for the mount point.
21749336Sdfr */
21759336Sdfrvoid
21769336Sdfrnfs_clearcommit(mp)
21779336Sdfr	struct mount *mp;
21789336Sdfr{
21799336Sdfr	register struct vnode *vp, *nvp;
21809336Sdfr	register struct buf *bp, *nbp;
21819336Sdfr	int s;
21829336Sdfr
21839336Sdfr	s = splbio();
21849336Sdfrloop:
21859336Sdfr	for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
21869336Sdfr		if (vp->v_mount != mp)	/* Paranoia */
21879336Sdfr			goto loop;
21889336Sdfr		nvp = vp->v_mntvnodes.le_next;
218940790Speter		for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
219040790Speter			nbp = TAILQ_NEXT(bp, b_vnbufs);
219148225Smckusick			if (BUF_REFCNT(bp) == 0 &&
219248225Smckusick			    (bp->b_flags & (B_DELWRI | B_NEEDCOMMIT))
21939336Sdfr				== (B_DELWRI | B_NEEDCOMMIT))
21949336Sdfr				bp->b_flags &= ~B_NEEDCOMMIT;
21959336Sdfr		}
21969336Sdfr	}
21979336Sdfr	splx(s);
21989336Sdfr}
21999336Sdfr
220013416Sphk#ifndef NFS_NOSERVER
22019336Sdfr/*
22029336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
22039336Sdfr * numbers not specified for the associated procedure.
22049336Sdfr */
22055455Sdgint
22069336Sdfrnfsrv_errmap(nd, err)
22079336Sdfr	struct nfsrv_descript *nd;
22089336Sdfr	register int err;
22099336Sdfr{
22109336Sdfr	register short *defaulterrp, *errp;
22119336Sdfr
22129336Sdfr	if (nd->nd_flag & ND_NFSV3) {
22139336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
22149336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
22159336Sdfr		while (*++errp) {
22169336Sdfr			if (*errp == err)
22179336Sdfr				return (err);
22189336Sdfr			else if (*errp > err)
22199336Sdfr				break;
22209336Sdfr		}
22219336Sdfr		return ((int)*defaulterrp);
22229336Sdfr	    } else
22239336Sdfr		return (err & 0xffff);
22249336Sdfr	}
22259336Sdfr	if (err <= ELAST)
22269336Sdfr		return ((int)nfsrv_v2errmap[err - 1]);
22279336Sdfr	return (NFSERR_IO);
22289336Sdfr}
22299336Sdfr
22309336Sdfrint
223131886Sbdenfsrv_object_create(vp)
223231886Sbde	struct vnode *vp;
223331886Sbde{
22345455Sdg
223531886Sbde	if (vp == NULL || vp->v_type != VREG)
223631886Sbde		return (1);
223731886Sbde	return (vfs_object_create(vp, curproc,
223842315Seivind				  curproc ? curproc->p_ucred : NULL));
22395455Sdg}
224036503Speter
224136503Speter/*
224236503Speter * Sort the group list in increasing numerical order.
224336503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
224436503Speter *  that used to be here.)
224536503Speter */
224636503Spetervoid
224736503Speternfsrvw_sort(list, num)
224836503Speter        register gid_t *list;
224936503Speter        register int num;
225036503Speter{
225136503Speter	register int i, j;
225236503Speter	gid_t v;
225336503Speter
225436503Speter	/* Insertion sort. */
225536503Speter	for (i = 1; i < num; i++) {
225636503Speter		v = list[i];
225736503Speter		/* find correct slot for value v, moving others up */
225836503Speter		for (j = i; --j >= 0 && v < list[j];)
225936503Speter			list[j + 1] = list[j];
226036503Speter		list[j + 1] = v;
226136503Speter	}
226236503Speter}
226336503Speter
226436503Speter/*
226536503Speter * copy credentials making sure that the result can be compared with bcmp().
226636503Speter */
226736503Spetervoid
226836503Speternfsrv_setcred(incred, outcred)
226936503Speter	register struct ucred *incred, *outcred;
227036503Speter{
227136503Speter	register int i;
227236503Speter
227336503Speter	bzero((caddr_t)outcred, sizeof (struct ucred));
227436503Speter	outcred->cr_ref = 1;
227536503Speter	outcred->cr_uid = incred->cr_uid;
227636503Speter	outcred->cr_ngroups = incred->cr_ngroups;
227736503Speter	for (i = 0; i < incred->cr_ngroups; i++)
227836503Speter		outcred->cr_groups[i] = incred->cr_groups[i];
227936503Speter	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
228036503Speter}
228113416Sphk#endif /* NFS_NOSERVER */
2282