xref: /freebsd/sys/fs/nfs/nfs_commonsubs.c (revision b97a478896e9523245c2041b064121ccb1f70426)
19ec7b004SRick Macklem /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
49ec7b004SRick Macklem  * Copyright (c) 1989, 1993
59ec7b004SRick Macklem  *	The Regents of the University of California.  All rights reserved.
69ec7b004SRick Macklem  *
79ec7b004SRick Macklem  * This code is derived from software contributed to Berkeley by
89ec7b004SRick Macklem  * Rick Macklem at The University of Guelph.
99ec7b004SRick Macklem  *
109ec7b004SRick Macklem  * Redistribution and use in source and binary forms, with or without
119ec7b004SRick Macklem  * modification, are permitted provided that the following conditions
129ec7b004SRick Macklem  * are met:
139ec7b004SRick Macklem  * 1. Redistributions of source code must retain the above copyright
149ec7b004SRick Macklem  *    notice, this list of conditions and the following disclaimer.
159ec7b004SRick Macklem  * 2. Redistributions in binary form must reproduce the above copyright
169ec7b004SRick Macklem  *    notice, this list of conditions and the following disclaimer in the
179ec7b004SRick Macklem  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
199ec7b004SRick Macklem  *    may be used to endorse or promote products derived from this software
209ec7b004SRick Macklem  *    without specific prior written permission.
219ec7b004SRick Macklem  *
229ec7b004SRick Macklem  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239ec7b004SRick Macklem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249ec7b004SRick Macklem  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259ec7b004SRick Macklem  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269ec7b004SRick Macklem  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279ec7b004SRick Macklem  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289ec7b004SRick Macklem  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299ec7b004SRick Macklem  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309ec7b004SRick Macklem  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319ec7b004SRick Macklem  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329ec7b004SRick Macklem  * SUCH DAMAGE.
339ec7b004SRick Macklem  *
349ec7b004SRick Macklem  */
359ec7b004SRick Macklem 
369ec7b004SRick Macklem #include <sys/cdefs.h>
379ec7b004SRick Macklem /*
389ec7b004SRick Macklem  * These functions support the macros and help fiddle mbuf chains for
399ec7b004SRick Macklem  * the nfs op functions. They do things like create the rpc header and
409ec7b004SRick Macklem  * copy data between mbuf chains and uio lists.
419ec7b004SRick Macklem  */
4280405bcfSRick Macklem #include "opt_inet.h"
43f7258644SRick Macklem #include "opt_inet6.h"
44f7258644SRick Macklem 
459ec7b004SRick Macklem #include <fs/nfs/nfsport.h>
46896516e5SRick Macklem #include <fs/nfsclient/nfsmount.h>
479ec7b004SRick Macklem 
48c057a378SRick Macklem #include <sys/extattr.h>
49c057a378SRick Macklem 
5084be7e09SRick Macklem #include <security/mac/mac_framework.h>
5184be7e09SRick Macklem 
524476c1deSRick Macklem #include <vm/vm_param.h>
534476c1deSRick Macklem 
549ec7b004SRick Macklem /*
559ec7b004SRick Macklem  * Data items converted to xdr at startup, since they are constant
569ec7b004SRick Macklem  * This is kinda hokey, but may save a little time doing byte swaps
579ec7b004SRick Macklem  */
589ec7b004SRick Macklem u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
599ec7b004SRick Macklem 
609ec7b004SRick Macklem /* And other global data */
619ec7b004SRick Macklem nfstype nfsv34_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
629ec7b004SRick Macklem 		      NFFIFO, NFNON };
63ba8cc6d7SMateusz Guzik __enum_uint8(vtype) newnv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON };
64ba8cc6d7SMateusz Guzik __enum_uint8(vtype) nv34tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
659ec7b004SRick Macklem struct timeval nfsboottime;	/* Copy boottime once, so it never changes */
669ec7b004SRick Macklem int nfscl_ticks;
679ec7b004SRick Macklem int nfsrv_useacl = 1;
689ec7b004SRick Macklem struct nfsreqhead nfsd_reqq;
699ec7b004SRick Macklem int nfsrv_lease = NFSRV_LEASE;
709ec7b004SRick Macklem int ncl_mbuf_mlen = MLEN;
7190d2dfabSRick Macklem int nfsrv_doflexfile = 0;
729ec7b004SRick Macklem NFSNAMEIDMUTEX;
739ec7b004SRick Macklem NFSSOCKMUTEX;
7484be7e09SRick Macklem extern int nfsrv_lughashsize;
7590d2dfabSRick Macklem extern struct mtx nfsrv_dslock_mtx;
7690d2dfabSRick Macklem extern volatile int nfsrv_devidcnt;
7790d2dfabSRick Macklem extern int nfscl_debuglevel;
7890d2dfabSRick Macklem extern struct nfsdevicehead nfsrv_devidhead;
79c338c94dSRick Macklem extern struct nfsstatsv1 nfsstatsv1;
80ee29e6f3SRick Macklem extern uint32_t nfs_srvmaxio;
819ec7b004SRick Macklem 
82f0db2b60SRick Macklem NFSD_VNET_DEFINE(int, nfsd_enable_stringtouid) = 0;
83f0db2b60SRick Macklem NFSD_VNET_DEFINE(struct nfssockreq, nfsrv_nfsuserdsock);
84f0db2b60SRick Macklem NFSD_VNET_DEFINE(nfsuserd_state, nfsrv_nfsuserd) = NOTRUNNING;
85f0db2b60SRick Macklem NFSD_VNET_DEFINE(uid_t, nfsrv_defaultuid) = UID_NOBODY;
86f0db2b60SRick Macklem NFSD_VNET_DEFINE(gid_t, nfsrv_defaultgid) = GID_NOGROUP;
87f0db2b60SRick Macklem 
88f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(int, nfsrv_userdupcalls) = 0;
89f0db2b60SRick Macklem 
901d2fef9bSEdward Tomasz Napierala SYSCTL_DECL(_vfs_nfs);
91f0db2b60SRick Macklem 
92f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(int, nfs_enable_uidtostring) = 0;
93f0db2b60SRick Macklem SYSCTL_INT(_vfs_nfs, OID_AUTO, enable_uidtostring,
94f0db2b60SRick Macklem     CTLFLAG_NFSD_VNET | CTLFLAG_RW, &NFSD_VNET_NAME(nfs_enable_uidtostring), 0,
95f0db2b60SRick Macklem     "Make nfs always send numeric owner_names");
961d2fef9bSEdward Tomasz Napierala 
9790d2dfabSRick Macklem int nfsrv_maxpnfsmirror = 1;
9890d2dfabSRick Macklem SYSCTL_INT(_vfs_nfs, OID_AUTO, pnfsmirror, CTLFLAG_RD,
9990d2dfabSRick Macklem     &nfsrv_maxpnfsmirror, 0, "Mirror level for pNFS service");
10090d2dfabSRick Macklem 
1019ec7b004SRick Macklem /*
1029ec7b004SRick Macklem  * This array of structures indicates, for V4:
1039ec7b004SRick Macklem  * retfh - which of 3 types of calling args are used
1049ec7b004SRick Macklem  *	0 - doesn't change cfh or use a sfh
1059ec7b004SRick Macklem  *	1 - replaces cfh with a new one (unless it returns an error status)
1069ec7b004SRick Macklem  *	2 - uses cfh and sfh
1079ec7b004SRick Macklem  * needscfh - if the op wants a cfh and premtime
1089ec7b004SRick Macklem  *	0 - doesn't use a cfh
1099ec7b004SRick Macklem  *	1 - uses a cfh, but doesn't want pre-op attributes
1109ec7b004SRick Macklem  *	2 - uses a cfh and wants pre-op attributes
1119ec7b004SRick Macklem  * savereply - indicates a non-idempotent Op
1129ec7b004SRick Macklem  *	0 - not non-idempotent
1139ec7b004SRick Macklem  *	1 - non-idempotent
1149ec7b004SRick Macklem  * Ops that are ordered via seqid# are handled separately from these
1159ec7b004SRick Macklem  * non-idempotent Ops.
1169ec7b004SRick Macklem  * Define it here, since it is used by both the client and server.
1179ec7b004SRick Macklem  */
118c057a378SRick Macklem struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS] = {
119b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* undef */
120b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* undef */
121b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* undef */
122b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Access */
123b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Close */
124b2fc0141SRick Macklem 	{ 0, 2, 0, 1, LK_EXCLUSIVE, 1, 1 },		/* Commit */
125b2fc0141SRick Macklem 	{ 1, 2, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Create */
126b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Delegpurge */
127b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Delegreturn */
128b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Getattr */
129b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* GetFH */
130b2fc0141SRick Macklem 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Link */
131b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Lock */
132b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* LockT */
133b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* LockU */
134b2fc0141SRick Macklem 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Lookup */
135b2fc0141SRick Macklem 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Lookupp */
136b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* NVerify */
137b2fc0141SRick Macklem 	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1, 0 },		/* Open */
138b2fc0141SRick Macklem 	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenAttr */
139b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenConfirm */
140b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenDowngrade */
141b2fc0141SRick Macklem 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutFH */
142b2fc0141SRick Macklem 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutPubFH */
143b2fc0141SRick Macklem 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutRootFH */
144b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 0 },		/* Read */
145b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Readdir */
146b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* ReadLink */
147b2fc0141SRick Macklem 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Remove */
148b2fc0141SRick Macklem 	{ 2, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Rename */
149b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Renew */
150b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* RestoreFH */
151b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SaveFH */
152b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SecInfo */
153b2fc0141SRick Macklem 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Setattr */
154b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SetClientID */
155b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* SetClientIDConfirm */
156330aa8acSRick Macklem 	{ 0, 2, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Verify (AppWrite) */
157b2fc0141SRick Macklem 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Write */
158b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* ReleaseLockOwner */
159b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Backchannel Ctrl */
1609442a64eSRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Bind Conn to Sess */
161b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Exchange ID */
162b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Create Session */
163b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Destroy Session */
164b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Free StateID */
165b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Get Dir Deleg */
166b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Get Device Info */
167b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Get Device List */
1683e5ba2e1SRick Macklem 	{ 0, 1, 0, 1, LK_EXCLUSIVE, 1, 1 },		/* Layout Commit */
169b2fc0141SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Layout Get */
1703e5ba2e1SRick Macklem 	{ 0, 1, 0, 1, LK_EXCLUSIVE, 1, 0 },		/* Layout Return */
171947bd247SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Secinfo No name */
172b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Sequence */
173b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Set SSV */
174b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Test StateID */
175b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Want Delegation */
176b2fc0141SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Destroy ClientID */
1772242bc81SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Reclaim Complete */
178c057a378SRick Macklem 	{ 0, 1, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Allocate */
179c057a378SRick Macklem 	{ 2, 1, 1, 0, LK_SHARED, 1, 0 },		/* Copy */
180c057a378SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Copy Notify */
1813ad1e1c1SRick Macklem 	{ 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Deallocate */
182c057a378SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 0 },		/* IO Advise */
183c057a378SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Layout Error */
184c057a378SRick Macklem 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Layout Stats */
185c057a378SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Offload Cancel */
186c057a378SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Offload Status */
187c057a378SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Read Plus */
188c057a378SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 0 },		/* Seek */
189c057a378SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Write Same */
190c057a378SRick Macklem 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Clone */
191c057a378SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Getxattr */
192c057a378SRick Macklem 	{ 0, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Setxattr */
193c057a378SRick Macklem 	{ 0, 1, 0, 0, LK_SHARED, 1, 1 },		/* Listxattrs */
194c057a378SRick Macklem 	{ 0, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* Removexattr */
1959ec7b004SRick Macklem };
1969ec7b004SRick Macklem 
1979ec7b004SRick Macklem static int ncl_mbuf_mhlen = MHLEN;
19884be7e09SRick Macklem struct nfsrv_lughash {
19984be7e09SRick Macklem 	struct mtx		mtx;
20084be7e09SRick Macklem 	struct nfsuserhashhead	lughead;
20184be7e09SRick Macklem };
202f0db2b60SRick Macklem 
203f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(int, nfsrv_usercnt) = 0;
204f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(int, nfsrv_dnsnamelen) = 0;
205f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(int, nfsrv_usermax) = 999999999;
206f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(struct nfsrv_lughash *, nfsuserhash) = NULL;
207f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(struct nfsrv_lughash *, nfsusernamehash) = NULL;
208f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(struct nfsrv_lughash *, nfsgrouphash) = NULL;
209f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(struct nfsrv_lughash *, nfsgroupnamehash) = NULL;
210f0db2b60SRick Macklem NFSD_VNET_DEFINE_STATIC(u_char *, nfsrv_dnsname) = NULL;
2119ec7b004SRick Macklem 
2129ec7b004SRick Macklem /*
2139ec7b004SRick Macklem  * This static array indicates whether or not the RPC generates a large
2149ec7b004SRick Macklem  * reply. This is used by nfs_reply() to decide whether or not an mbuf
2159ec7b004SRick Macklem  * cluster should be allocated. (If a cluster is required by an RPC
2169ec7b004SRick Macklem  * marked 0 in this array, the code will still work, just not quite as
2179ec7b004SRick Macklem  * efficiently.)
2189ec7b004SRick Macklem  */
219c057a378SRick Macklem static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
2209ec7b004SRick Macklem     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
221c057a378SRick Macklem     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
222330aa8acSRick Macklem     1, 0, 0, 1, 0, 0, 0, 0, 0 };
2239ec7b004SRick Macklem 
2249ec7b004SRick Macklem /* local functions */
2259ec7b004SRick Macklem static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
2269ec7b004SRick Macklem static void nfsv4_wanted(struct nfsv4lock *lp);
2272d90ef47SRick Macklem static uint32_t nfsv4_filesavail(struct statfs *, struct mount *);
228f32bf292SEdward Tomasz Napierala static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name);
22984be7e09SRick Macklem static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
2309ec7b004SRick Macklem static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
2319ec7b004SRick Macklem     int *, int *);
2329ec7b004SRick Macklem static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
2339ec7b004SRick Macklem 
234c338c94dSRick Macklem static struct {
235c338c94dSRick Macklem 	int	op;
236c338c94dSRick Macklem 	int	opcnt;
237c338c94dSRick Macklem 	const u_char *tag;
238c338c94dSRick Macklem 	int	taglen;
239c057a378SRick Macklem } nfsv4_opmap[NFSV42_NPROCS] = {
240c338c94dSRick Macklem 	{ 0, 1, "Null", 4 },
241c338c94dSRick Macklem 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
242c338c94dSRick Macklem 	{ NFSV4OP_SETATTR, 2, "Setattr", 7, },
243c338c94dSRick Macklem 	{ NFSV4OP_LOOKUP, 3, "Lookup", 6, },
244c338c94dSRick Macklem 	{ NFSV4OP_ACCESS, 2, "Access", 6, },
245c338c94dSRick Macklem 	{ NFSV4OP_READLINK, 2, "Readlink", 8, },
246c338c94dSRick Macklem 	{ NFSV4OP_READ, 1, "Read", 4, },
247c338c94dSRick Macklem 	{ NFSV4OP_WRITE, 2, "Write", 5, },
248c338c94dSRick Macklem 	{ NFSV4OP_OPEN, 5, "Open", 4, },
249c338c94dSRick Macklem 	{ NFSV4OP_CREATE, 5, "Create", 6, },
250c338c94dSRick Macklem 	{ NFSV4OP_CREATE, 1, "Create", 6, },
251c338c94dSRick Macklem 	{ NFSV4OP_CREATE, 3, "Create", 6, },
252c338c94dSRick Macklem 	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
253c338c94dSRick Macklem 	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
254c338c94dSRick Macklem 	{ NFSV4OP_SAVEFH, 5, "Rename", 6, },
255c338c94dSRick Macklem 	{ NFSV4OP_SAVEFH, 4, "Link", 4, },
256c338c94dSRick Macklem 	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
257c338c94dSRick Macklem 	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
258c338c94dSRick Macklem 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
259c338c94dSRick Macklem 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
260c338c94dSRick Macklem 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
261c338c94dSRick Macklem 	{ NFSV4OP_COMMIT, 2, "Commit", 6, },
262c338c94dSRick Macklem 	{ NFSV4OP_LOOKUPP, 3, "Lookupp", 7, },
263c338c94dSRick Macklem 	{ NFSV4OP_SETCLIENTID, 1, "SetClientID", 11, },
264c338c94dSRick Macklem 	{ NFSV4OP_SETCLIENTIDCFRM, 1, "SetClientIDConfirm", 18, },
265c338c94dSRick Macklem 	{ NFSV4OP_LOCK, 1, "Lock", 4, },
266c338c94dSRick Macklem 	{ NFSV4OP_LOCKU, 1, "LockU", 5, },
267c338c94dSRick Macklem 	{ NFSV4OP_OPEN, 2, "Open", 4, },
268c338c94dSRick Macklem 	{ NFSV4OP_CLOSE, 1, "Close", 5, },
269c338c94dSRick Macklem 	{ NFSV4OP_OPENCONFIRM, 1, "Openconfirm", 11, },
270c338c94dSRick Macklem 	{ NFSV4OP_LOCKT, 1, "LockT", 5, },
271c338c94dSRick Macklem 	{ NFSV4OP_OPENDOWNGRADE, 1, "Opendowngrade", 13, },
272c338c94dSRick Macklem 	{ NFSV4OP_RENEW, 1, "Renew", 5, },
273c338c94dSRick Macklem 	{ NFSV4OP_PUTROOTFH, 1, "Dirpath", 7, },
274c338c94dSRick Macklem 	{ NFSV4OP_RELEASELCKOWN, 1, "Rellckown", 9, },
275c338c94dSRick Macklem 	{ NFSV4OP_DELEGRETURN, 1, "Delegret", 8, },
276c338c94dSRick Macklem 	{ NFSV4OP_DELEGRETURN, 3, "DelegRemove", 11, },
277c338c94dSRick Macklem 	{ NFSV4OP_DELEGRETURN, 7, "DelegRename1", 12, },
278c338c94dSRick Macklem 	{ NFSV4OP_DELEGRETURN, 9, "DelegRename2", 12, },
279c338c94dSRick Macklem 	{ NFSV4OP_GETATTR, 1, "Getacl", 6, },
280c338c94dSRick Macklem 	{ NFSV4OP_SETATTR, 1, "Setacl", 6, },
281c338c94dSRick Macklem 	{ NFSV4OP_EXCHANGEID, 1, "ExchangeID", 10, },
282c338c94dSRick Macklem 	{ NFSV4OP_CREATESESSION, 1, "CreateSession", 13, },
283c338c94dSRick Macklem 	{ NFSV4OP_DESTROYSESSION, 1, "DestroySession", 14, },
284c338c94dSRick Macklem 	{ NFSV4OP_DESTROYCLIENTID, 1, "DestroyClient", 13, },
285c338c94dSRick Macklem 	{ NFSV4OP_FREESTATEID, 1, "FreeStateID", 11, },
286c338c94dSRick Macklem 	{ NFSV4OP_LAYOUTGET, 1, "LayoutGet", 9, },
287c338c94dSRick Macklem 	{ NFSV4OP_GETDEVINFO, 1, "GetDeviceInfo", 13, },
288c338c94dSRick Macklem 	{ NFSV4OP_LAYOUTCOMMIT, 1, "LayoutCommit", 12, },
289c338c94dSRick Macklem 	{ NFSV4OP_LAYOUTRETURN, 1, "LayoutReturn", 12, },
290c338c94dSRick Macklem 	{ NFSV4OP_RECLAIMCOMPL, 1, "ReclaimComplete", 15, },
291c338c94dSRick Macklem 	{ NFSV4OP_WRITE, 1, "WriteDS", 7, },
292c338c94dSRick Macklem 	{ NFSV4OP_READ, 1, "ReadDS", 6, },
293c338c94dSRick Macklem 	{ NFSV4OP_COMMIT, 1, "CommitDS", 8, },
294c338c94dSRick Macklem 	{ NFSV4OP_OPEN, 3, "OpenLayoutGet", 13, },
295c338c94dSRick Macklem 	{ NFSV4OP_OPEN, 8, "CreateLayGet", 12, },
296c057a378SRick Macklem 	{ NFSV4OP_IOADVISE, 1, "Advise", 6, },
297c057a378SRick Macklem 	{ NFSV4OP_ALLOCATE, 2, "Allocate", 8, },
298cc760de2SRick Macklem 	{ NFSV4OP_SAVEFH, 5, "Copy", 4, },
299c057a378SRick Macklem 	{ NFSV4OP_SEEK, 2, "Seek", 4, },
300c057a378SRick Macklem 	{ NFSV4OP_SEEK, 1, "SeekDS", 6, },
301c057a378SRick Macklem 	{ NFSV4OP_GETXATTR, 2, "Getxattr", 8, },
302c057a378SRick Macklem 	{ NFSV4OP_SETXATTR, 2, "Setxattr", 8, },
303c057a378SRick Macklem 	{ NFSV4OP_REMOVEXATTR, 2, "Rmxattr", 7, },
304c057a378SRick Macklem 	{ NFSV4OP_LISTXATTRS, 2, "Listxattr", 9, },
3057763814fSRick Macklem 	{ NFSV4OP_BINDCONNTOSESS, 1, "BindConSess", 11, },
3063ad1e1c1SRick Macklem 	{ NFSV4OP_LOOKUP, 5, "LookupOpen", 10, },
3073ad1e1c1SRick Macklem 	{ NFSV4OP_DEALLOCATE, 2, "Deallocate", 10, },
30844744f75SRick Macklem 	{ NFSV4OP_LAYOUTERROR, 1, "LayoutError", 11, },
309330aa8acSRick Macklem 	{ NFSV4OP_VERIFY, 3, "AppendWrite", 11, },
310c338c94dSRick Macklem };
311c338c94dSRick Macklem 
312c338c94dSRick Macklem /*
313c338c94dSRick Macklem  * NFS RPCS that have large request message size.
314c338c94dSRick Macklem  */
315c057a378SRick Macklem static int nfs_bigrequest[NFSV42_NPROCS] = {
316c338c94dSRick Macklem 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
317c338c94dSRick Macklem 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31844744f75SRick Macklem 	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
319330aa8acSRick Macklem 	0, 1
320c338c94dSRick Macklem };
321c338c94dSRick Macklem 
322c338c94dSRick Macklem /*
323c338c94dSRick Macklem  * Start building a request. Mostly just put the first file handle in
324c338c94dSRick Macklem  * place.
325c338c94dSRick Macklem  */
326b9cc3262SRyan Moeller void
nfscl_reqstart(struct nfsrv_descript * nd,int procnum,struct nfsmount * nmp,u_int8_t * nfhp,int fhlen,u_int32_t ** opcntpp,struct nfsclsession * sep,int vers,int minorvers,struct ucred * cred)327c338c94dSRick Macklem nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
328c338c94dSRick Macklem     u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp, struct nfsclsession *sep,
32940ada74eSRick Macklem     int vers, int minorvers, struct ucred *cred)
330c338c94dSRick Macklem {
331c338c94dSRick Macklem 	struct mbuf *mb;
332c338c94dSRick Macklem 	u_int32_t *tl;
333c338c94dSRick Macklem 	int opcnt;
334c338c94dSRick Macklem 	nfsattrbit_t attrbits;
335c338c94dSRick Macklem 
336c338c94dSRick Macklem 	/*
337c338c94dSRick Macklem 	 * First, fill in some of the fields of nd.
338c338c94dSRick Macklem 	 */
339c338c94dSRick Macklem 	nd->nd_slotseq = NULL;
340c338c94dSRick Macklem 	if (vers == NFS_VER4) {
341c338c94dSRick Macklem 		nd->nd_flag = ND_NFSV4 | ND_NFSCL;
342c338c94dSRick Macklem 		if (minorvers == NFSV41_MINORVERSION)
343c338c94dSRick Macklem 			nd->nd_flag |= ND_NFSV41;
344c057a378SRick Macklem 		else if (minorvers == NFSV42_MINORVERSION)
345c057a378SRick Macklem 			nd->nd_flag |= (ND_NFSV41 | ND_NFSV42);
346c338c94dSRick Macklem 	} else if (vers == NFS_VER3)
347c338c94dSRick Macklem 		nd->nd_flag = ND_NFSV3 | ND_NFSCL;
348c338c94dSRick Macklem 	else {
349c338c94dSRick Macklem 		if (NFSHASNFSV4(nmp)) {
350c338c94dSRick Macklem 			nd->nd_flag = ND_NFSV4 | ND_NFSCL;
351c057a378SRick Macklem 			if (nmp->nm_minorvers == 1)
352c338c94dSRick Macklem 				nd->nd_flag |= ND_NFSV41;
353c057a378SRick Macklem 			else if (nmp->nm_minorvers == 2)
354c057a378SRick Macklem 				nd->nd_flag |= (ND_NFSV41 | ND_NFSV42);
355c338c94dSRick Macklem 		} else if (NFSHASNFSV3(nmp))
356c338c94dSRick Macklem 			nd->nd_flag = ND_NFSV3 | ND_NFSCL;
357c338c94dSRick Macklem 		else
358c338c94dSRick Macklem 			nd->nd_flag = ND_NFSV2 | ND_NFSCL;
359c338c94dSRick Macklem 	}
360c338c94dSRick Macklem 	nd->nd_procnum = procnum;
361c338c94dSRick Macklem 	nd->nd_repstat = 0;
362808306ddSRick Macklem 	nd->nd_maxextsiz = 0;
363c338c94dSRick Macklem 
364c338c94dSRick Macklem 	/*
365c338c94dSRick Macklem 	 * Get the first mbuf for the request.
366c338c94dSRick Macklem 	 */
367c338c94dSRick Macklem 	if (nfs_bigrequest[procnum])
368c338c94dSRick Macklem 		NFSMCLGET(mb, M_WAITOK);
369c338c94dSRick Macklem 	else
370c338c94dSRick Macklem 		NFSMGET(mb);
371c948a17aSRick Macklem 	mb->m_len = 0;
372c338c94dSRick Macklem 	nd->nd_mreq = nd->nd_mb = mb;
3734476c1deSRick Macklem 	nd->nd_bpos = mtod(mb, char *);
374c338c94dSRick Macklem 
3754adb28c0SRick Macklem 	/* For NFSPROC_NULL, there are no arguments. */
3764adb28c0SRick Macklem 	if (procnum == NFSPROC_NULL)
3774adb28c0SRick Macklem 		goto out;
3784adb28c0SRick Macklem 
379c338c94dSRick Macklem 	/*
380c338c94dSRick Macklem 	 * And fill the first file handle into the request.
381c338c94dSRick Macklem 	 */
382c338c94dSRick Macklem 	if (nd->nd_flag & ND_NFSV4) {
383c338c94dSRick Macklem 		opcnt = nfsv4_opmap[procnum].opcnt +
384c338c94dSRick Macklem 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
385c338c94dSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0) {
386c338c94dSRick Macklem 			opcnt += nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq;
387c338c94dSRick Macklem 			if (procnum == NFSPROC_RENEW)
388c338c94dSRick Macklem 				/*
389c338c94dSRick Macklem 				 * For the special case of Renew, just do a
390c338c94dSRick Macklem 				 * Sequence Op.
391c338c94dSRick Macklem 				 */
392c338c94dSRick Macklem 				opcnt = 1;
393c338c94dSRick Macklem 			else if (procnum == NFSPROC_WRITEDS ||
394c338c94dSRick Macklem 			    procnum == NFSPROC_COMMITDS)
395c338c94dSRick Macklem 				/*
396c338c94dSRick Macklem 				 * For the special case of a Writeor Commit to
397c338c94dSRick Macklem 				 * a DS, the opcnt == 3, for Sequence, PutFH,
398c338c94dSRick Macklem 				 * Write/Commit.
399c338c94dSRick Macklem 				 */
400c338c94dSRick Macklem 				opcnt = 3;
401c338c94dSRick Macklem 		}
402c338c94dSRick Macklem 		/*
403c338c94dSRick Macklem 		 * What should the tag really be?
404c338c94dSRick Macklem 		 */
405c338c94dSRick Macklem 		(void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
406c338c94dSRick Macklem 			nfsv4_opmap[procnum].taglen);
407c338c94dSRick Macklem 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
408c057a378SRick Macklem 		if ((nd->nd_flag & ND_NFSV42) != 0)
409c057a378SRick Macklem 			*tl++ = txdr_unsigned(NFSV42_MINORVERSION);
410c057a378SRick Macklem 		else if ((nd->nd_flag & ND_NFSV41) != 0)
411c338c94dSRick Macklem 			*tl++ = txdr_unsigned(NFSV41_MINORVERSION);
412c338c94dSRick Macklem 		else
413c338c94dSRick Macklem 			*tl++ = txdr_unsigned(NFSV4_MINORVERSION);
414c338c94dSRick Macklem 		if (opcntpp != NULL)
415c338c94dSRick Macklem 			*opcntpp = tl;
416c338c94dSRick Macklem 		*tl = txdr_unsigned(opcnt);
417c338c94dSRick Macklem 		if ((nd->nd_flag & ND_NFSV41) != 0 &&
418c338c94dSRick Macklem 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
419c338c94dSRick Macklem 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
420c338c94dSRick Macklem 			    0)
421c338c94dSRick Macklem 				nd->nd_flag |= ND_LOOPBADSESS;
422c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
423c338c94dSRick Macklem 			*tl = txdr_unsigned(NFSV4OP_SEQUENCE);
424c338c94dSRick Macklem 			if (sep == NULL) {
425c338c94dSRick Macklem 				sep = nfsmnt_mdssession(nmp);
42640ada74eSRick Macklem 				/*
42740ada74eSRick Macklem 				 * For MDS mount sessions, check for bad
42840ada74eSRick Macklem 				 * slots.  If the caller does not want this
42940ada74eSRick Macklem 				 * check to be done, the "cred" argument can
43040ada74eSRick Macklem 				 * be passed in as NULL.
43140ada74eSRick Macklem 				 */
432c338c94dSRick Macklem 				nfsv4_setsequence(nmp, nd, sep,
43340ada74eSRick Macklem 				    nfs_bigreply[procnum], cred);
434c338c94dSRick Macklem 			} else
435c338c94dSRick Macklem 				nfsv4_setsequence(nmp, nd, sep,
43640ada74eSRick Macklem 				    nfs_bigreply[procnum], NULL);
437c338c94dSRick Macklem 		}
438c338c94dSRick Macklem 		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
439c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
440c338c94dSRick Macklem 			*tl = txdr_unsigned(NFSV4OP_PUTFH);
441695d87baSRick Macklem 			(void)nfsm_fhtom(nmp, nd, nfhp, fhlen, 0);
442c338c94dSRick Macklem 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
443c338c94dSRick Macklem 			    == 2 && procnum != NFSPROC_WRITEDS &&
444c338c94dSRick Macklem 			    procnum != NFSPROC_COMMITDS) {
445c338c94dSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
446c338c94dSRick Macklem 				*tl = txdr_unsigned(NFSV4OP_GETATTR);
447c338c94dSRick Macklem 				/*
448c338c94dSRick Macklem 				 * For Lookup Ops, we want all the directory
449c338c94dSRick Macklem 				 * attributes, so we can load the name cache.
450c338c94dSRick Macklem 				 */
451c338c94dSRick Macklem 				if (procnum == NFSPROC_LOOKUP ||
4523ad1e1c1SRick Macklem 				    procnum == NFSPROC_LOOKUPP ||
4533ad1e1c1SRick Macklem 				    procnum == NFSPROC_LOOKUPOPEN)
454c338c94dSRick Macklem 					NFSGETATTR_ATTRBIT(&attrbits);
455c338c94dSRick Macklem 				else {
456c338c94dSRick Macklem 					NFSWCCATTR_ATTRBIT(&attrbits);
45721de450aSRick Macklem 					/* For AppendWrite, get the size. */
45821de450aSRick Macklem 					if (procnum == NFSPROC_APPENDWRITE)
45921de450aSRick Macklem 						NFSSETBIT_ATTRBIT(&attrbits,
46021de450aSRick Macklem 						    NFSATTRBIT_SIZE);
461c338c94dSRick Macklem 					nd->nd_flag |= ND_V4WCCATTR;
462c338c94dSRick Macklem 				}
463c338c94dSRick Macklem 				(void) nfsrv_putattrbit(nd, &attrbits);
464c338c94dSRick Macklem 			}
465c338c94dSRick Macklem 		}
466c338c94dSRick Macklem 		if (procnum != NFSPROC_RENEW ||
467c338c94dSRick Macklem 		    (nd->nd_flag & ND_NFSV41) == 0) {
468c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
469c338c94dSRick Macklem 			*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
470c338c94dSRick Macklem 		}
471c338c94dSRick Macklem 	} else {
472695d87baSRick Macklem 		(void)nfsm_fhtom(NULL, nd, nfhp, fhlen, 0);
473c338c94dSRick Macklem 	}
4744adb28c0SRick Macklem out:
475c057a378SRick Macklem 	if (procnum < NFSV42_NPROCS)
476c338c94dSRick Macklem 		NFSINCRGLOBAL(nfsstatsv1.rpccnt[procnum]);
477c338c94dSRick Macklem }
478c338c94dSRick Macklem 
479c338c94dSRick Macklem /*
480c338c94dSRick Macklem  * Put a state Id in the mbuf list.
481c338c94dSRick Macklem  */
482b9cc3262SRyan Moeller void
nfsm_stateidtom(struct nfsrv_descript * nd,nfsv4stateid_t * stateidp,int flag)483c338c94dSRick Macklem nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
484c338c94dSRick Macklem {
485c338c94dSRick Macklem 	nfsv4stateid_t *st;
486c338c94dSRick Macklem 
487c338c94dSRick Macklem 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
488c338c94dSRick Macklem 	if (flag == NFSSTATEID_PUTALLZERO) {
489c338c94dSRick Macklem 		st->seqid = 0;
490c338c94dSRick Macklem 		st->other[0] = 0;
491c338c94dSRick Macklem 		st->other[1] = 0;
492c338c94dSRick Macklem 		st->other[2] = 0;
493c338c94dSRick Macklem 	} else if (flag == NFSSTATEID_PUTALLONE) {
494c338c94dSRick Macklem 		st->seqid = 0xffffffff;
495c338c94dSRick Macklem 		st->other[0] = 0xffffffff;
496c338c94dSRick Macklem 		st->other[1] = 0xffffffff;
497c338c94dSRick Macklem 		st->other[2] = 0xffffffff;
498c338c94dSRick Macklem 	} else if (flag == NFSSTATEID_PUTSEQIDZERO) {
499c338c94dSRick Macklem 		st->seqid = 0;
500c338c94dSRick Macklem 		st->other[0] = stateidp->other[0];
501c338c94dSRick Macklem 		st->other[1] = stateidp->other[1];
502c338c94dSRick Macklem 		st->other[2] = stateidp->other[2];
503c338c94dSRick Macklem 	} else {
504c338c94dSRick Macklem 		st->seqid = stateidp->seqid;
505c338c94dSRick Macklem 		st->other[0] = stateidp->other[0];
506c338c94dSRick Macklem 		st->other[1] = stateidp->other[1];
507c338c94dSRick Macklem 		st->other[2] = stateidp->other[2];
508c338c94dSRick Macklem 	}
509c338c94dSRick Macklem }
510c338c94dSRick Macklem 
511c338c94dSRick Macklem /*
512c338c94dSRick Macklem  * Fill in the setable attributes. The full argument indicates whether
513c338c94dSRick Macklem  * to fill in them all or just mode and time.
514c338c94dSRick Macklem  */
515c338c94dSRick Macklem void
nfscl_fillsattr(struct nfsrv_descript * nd,struct vattr * vap,struct vnode * vp,int flags,u_int32_t rdev)516c338c94dSRick Macklem nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
517c338c94dSRick Macklem     struct vnode *vp, int flags, u_int32_t rdev)
518c338c94dSRick Macklem {
519c338c94dSRick Macklem 	u_int32_t *tl;
520c338c94dSRick Macklem 	struct nfsv2_sattr *sp;
521c338c94dSRick Macklem 	nfsattrbit_t attrbits;
522194d8704SRick Macklem 	struct nfsnode *np;
523c338c94dSRick Macklem 
524c338c94dSRick Macklem 	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
525c338c94dSRick Macklem 	case ND_NFSV2:
526c338c94dSRick Macklem 		NFSM_BUILD(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
527c338c94dSRick Macklem 		if (vap->va_mode == (mode_t)VNOVAL)
528c338c94dSRick Macklem 			sp->sa_mode = newnfs_xdrneg1;
529c338c94dSRick Macklem 		else
530c338c94dSRick Macklem 			sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
531c338c94dSRick Macklem 		if (vap->va_uid == (uid_t)VNOVAL)
532c338c94dSRick Macklem 			sp->sa_uid = newnfs_xdrneg1;
533c338c94dSRick Macklem 		else
534c338c94dSRick Macklem 			sp->sa_uid = txdr_unsigned(vap->va_uid);
535c338c94dSRick Macklem 		if (vap->va_gid == (gid_t)VNOVAL)
536c338c94dSRick Macklem 			sp->sa_gid = newnfs_xdrneg1;
537c338c94dSRick Macklem 		else
538c338c94dSRick Macklem 			sp->sa_gid = txdr_unsigned(vap->va_gid);
539c338c94dSRick Macklem 		if (flags & NFSSATTR_SIZE0)
540c338c94dSRick Macklem 			sp->sa_size = 0;
541c338c94dSRick Macklem 		else if (flags & NFSSATTR_SIZENEG1)
542c338c94dSRick Macklem 			sp->sa_size = newnfs_xdrneg1;
543c338c94dSRick Macklem 		else if (flags & NFSSATTR_SIZERDEV)
544c338c94dSRick Macklem 			sp->sa_size = txdr_unsigned(rdev);
545c338c94dSRick Macklem 		else
546c338c94dSRick Macklem 			sp->sa_size = txdr_unsigned(vap->va_size);
547c338c94dSRick Macklem 		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
548c338c94dSRick Macklem 		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
549c338c94dSRick Macklem 		break;
550c338c94dSRick Macklem 	case ND_NFSV3:
551c338c94dSRick Macklem 		if (vap->va_mode != (mode_t)VNOVAL) {
552c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
553c338c94dSRick Macklem 			*tl++ = newnfs_true;
554c338c94dSRick Macklem 			*tl = txdr_unsigned(vap->va_mode);
555c338c94dSRick Macklem 		} else {
556c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
557c338c94dSRick Macklem 			*tl = newnfs_false;
558c338c94dSRick Macklem 		}
559c338c94dSRick Macklem 		if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL) {
560c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
561c338c94dSRick Macklem 			*tl++ = newnfs_true;
562c338c94dSRick Macklem 			*tl = txdr_unsigned(vap->va_uid);
563c338c94dSRick Macklem 		} else {
564c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
565c338c94dSRick Macklem 			*tl = newnfs_false;
566c338c94dSRick Macklem 		}
567c338c94dSRick Macklem 		if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL) {
568c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
569c338c94dSRick Macklem 			*tl++ = newnfs_true;
570c338c94dSRick Macklem 			*tl = txdr_unsigned(vap->va_gid);
571c338c94dSRick Macklem 		} else {
572c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
573c338c94dSRick Macklem 			*tl = newnfs_false;
574c338c94dSRick Macklem 		}
575c338c94dSRick Macklem 		if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL) {
576c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
577c338c94dSRick Macklem 			*tl++ = newnfs_true;
578c338c94dSRick Macklem 			txdr_hyper(vap->va_size, tl);
579c338c94dSRick Macklem 		} else {
580c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
581c338c94dSRick Macklem 			*tl = newnfs_false;
582c338c94dSRick Macklem 		}
583c338c94dSRick Macklem 		if (vap->va_atime.tv_sec != VNOVAL) {
584c338c94dSRick Macklem 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
585c338c94dSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
586c338c94dSRick Macklem 				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
587c338c94dSRick Macklem 				txdr_nfsv3time(&vap->va_atime, tl);
588c338c94dSRick Macklem 			} else {
589c338c94dSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
590c338c94dSRick Macklem 				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
591c338c94dSRick Macklem 			}
592c338c94dSRick Macklem 		} else {
593c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
594c338c94dSRick Macklem 			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
595c338c94dSRick Macklem 		}
596c338c94dSRick Macklem 		if (vap->va_mtime.tv_sec != VNOVAL) {
597c338c94dSRick Macklem 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
598c338c94dSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
599c338c94dSRick Macklem 				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
600c338c94dSRick Macklem 				txdr_nfsv3time(&vap->va_mtime, tl);
601c338c94dSRick Macklem 			} else {
602c338c94dSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
603c338c94dSRick Macklem 				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
604c338c94dSRick Macklem 			}
605c338c94dSRick Macklem 		} else {
606c338c94dSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
607c338c94dSRick Macklem 			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
608c338c94dSRick Macklem 		}
609c338c94dSRick Macklem 		break;
610c338c94dSRick Macklem 	case ND_NFSV4:
611c338c94dSRick Macklem 		NFSZERO_ATTRBIT(&attrbits);
6122477e88bSRick Macklem 		np = NULL;
6132477e88bSRick Macklem 		if (strcmp(vp->v_mount->mnt_vfc->vfc_name, "nfs") == 0)
6142477e88bSRick Macklem 			np = VTONFS(vp);
6152477e88bSRick Macklem 		if (vap->va_mode != (mode_t)VNOVAL) {
6162477e88bSRick Macklem 			if ((flags & NFSSATTR_NEWFILE) != 0 && np != NULL &&
6172477e88bSRick Macklem 			    NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
6182477e88bSRick Macklem 			    NFSATTRBIT_MODEUMASK))
6192477e88bSRick Macklem 				NFSSETBIT_ATTRBIT(&attrbits,
6202477e88bSRick Macklem 				    NFSATTRBIT_MODEUMASK);
6212477e88bSRick Macklem 			else
622c338c94dSRick Macklem 				NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_MODE);
6232477e88bSRick Macklem 		}
624c338c94dSRick Macklem 		if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL)
625c338c94dSRick Macklem 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
626c338c94dSRick Macklem 		if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL)
627c338c94dSRick Macklem 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP);
628c338c94dSRick Macklem 		if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL)
629c338c94dSRick Macklem 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SIZE);
630c338c94dSRick Macklem 		if (vap->va_atime.tv_sec != VNOVAL)
631c338c94dSRick Macklem 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
632c338c94dSRick Macklem 		if (vap->va_mtime.tv_sec != VNOVAL)
633c338c94dSRick Macklem 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET);
634194d8704SRick Macklem 		/*
635194d8704SRick Macklem 		 * We can only test for support of TimeCreate if
636194d8704SRick Macklem 		 * the "vp" argument is for an NFS vnode.
637194d8704SRick Macklem 		 */
6382477e88bSRick Macklem 		if (vap->va_birthtime.tv_sec != VNOVAL && np != NULL &&
6392477e88bSRick Macklem 		    NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
640194d8704SRick Macklem 		    NFSATTRBIT_TIMECREATE))
6412477e88bSRick Macklem 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
642c338c94dSRick Macklem 		(void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0,
643c338c94dSRick Macklem 		    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL);
644c338c94dSRick Macklem 		break;
645c338c94dSRick Macklem 	}
646c338c94dSRick Macklem }
6479ec7b004SRick Macklem 
6489ec7b004SRick Macklem /*
6499ec7b004SRick Macklem  * copies mbuf chain to the uio scatter/gather list
6509ec7b004SRick Macklem  */
6519ec7b004SRick Macklem int
nfsm_mbufuio(struct nfsrv_descript * nd,struct uio * uiop,int siz)6529ec7b004SRick Macklem nfsm_mbufuio(struct nfsrv_descript *nd, struct uio *uiop, int siz)
6539ec7b004SRick Macklem {
6549ec7b004SRick Macklem 	char *mbufcp, *uiocp;
6559ec7b004SRick Macklem 	int xfer, left, len;
656ae070589SRick Macklem 	struct mbuf *mp;
6579ec7b004SRick Macklem 	long uiosiz, rem;
6589ec7b004SRick Macklem 	int error = 0;
6599ec7b004SRick Macklem 
6609ec7b004SRick Macklem 	mp = nd->nd_md;
6619ec7b004SRick Macklem 	mbufcp = nd->nd_dpos;
662c948a17aSRick Macklem 	len = mtod(mp, caddr_t) + mp->m_len - mbufcp;
6639ec7b004SRick Macklem 	rem = NFSM_RNDUP(siz) - siz;
6649ec7b004SRick Macklem 	while (siz > 0) {
665a9285ae5SZack Kirsch 		if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL) {
666a9285ae5SZack Kirsch 			error = EBADRPC;
667a9285ae5SZack Kirsch 			goto out;
668a9285ae5SZack Kirsch 		}
6699ec7b004SRick Macklem 		left = uiop->uio_iov->iov_len;
6709ec7b004SRick Macklem 		uiocp = uiop->uio_iov->iov_base;
6719ec7b004SRick Macklem 		if (left > siz)
6729ec7b004SRick Macklem 			left = siz;
6739ec7b004SRick Macklem 		uiosiz = left;
6749ec7b004SRick Macklem 		while (left > 0) {
6759ec7b004SRick Macklem 			while (len == 0) {
676c948a17aSRick Macklem 				mp = mp->m_next;
677a9285ae5SZack Kirsch 				if (mp == NULL) {
678a9285ae5SZack Kirsch 					error = EBADRPC;
679a9285ae5SZack Kirsch 					goto out;
680a9285ae5SZack Kirsch 				}
681c948a17aSRick Macklem 				mbufcp = mtod(mp, caddr_t);
682c948a17aSRick Macklem 				len = mp->m_len;
6836d659a5dSBenno Rice 				KASSERT(len >= 0,
6846d659a5dSBenno Rice 				    ("len %d, corrupted mbuf?", len));
6859ec7b004SRick Macklem 			}
6869ec7b004SRick Macklem 			xfer = (left > len) ? len : left;
6879ec7b004SRick Macklem 			if (uiop->uio_segflg == UIO_SYSSPACE)
6889ec7b004SRick Macklem 				NFSBCOPY(mbufcp, uiocp, xfer);
689b484bcd5SRick Macklem 			else {
690b484bcd5SRick Macklem 				error = copyout(mbufcp, uiocp, xfer);
691b484bcd5SRick Macklem 				if (error != 0)
692b484bcd5SRick Macklem 					goto out;
693b484bcd5SRick Macklem 			}
6949ec7b004SRick Macklem 			left -= xfer;
6959ec7b004SRick Macklem 			len -= xfer;
6969ec7b004SRick Macklem 			mbufcp += xfer;
6979ec7b004SRick Macklem 			uiocp += xfer;
6989ec7b004SRick Macklem 			uiop->uio_offset += xfer;
6999ec7b004SRick Macklem 			uiop->uio_resid -= xfer;
7009ec7b004SRick Macklem 		}
7019ec7b004SRick Macklem 		if (uiop->uio_iov->iov_len <= siz) {
7029ec7b004SRick Macklem 			uiop->uio_iovcnt--;
7039ec7b004SRick Macklem 			uiop->uio_iov++;
7049ec7b004SRick Macklem 		} else {
7059ec7b004SRick Macklem 			uiop->uio_iov->iov_base = (void *)
7069ec7b004SRick Macklem 				((char *)uiop->uio_iov->iov_base + uiosiz);
7079ec7b004SRick Macklem 			uiop->uio_iov->iov_len -= uiosiz;
7089ec7b004SRick Macklem 		}
7099ec7b004SRick Macklem 		siz -= uiosiz;
7109ec7b004SRick Macklem 	}
7119ec7b004SRick Macklem 	nd->nd_dpos = mbufcp;
7129ec7b004SRick Macklem 	nd->nd_md = mp;
7139ec7b004SRick Macklem 	if (rem > 0) {
7149ec7b004SRick Macklem 		if (len < rem)
7159ec7b004SRick Macklem 			error = nfsm_advance(nd, rem, len);
7169ec7b004SRick Macklem 		else
7179ec7b004SRick Macklem 			nd->nd_dpos += rem;
7189ec7b004SRick Macklem 	}
719a9285ae5SZack Kirsch 
720a9285ae5SZack Kirsch out:
721a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
7229ec7b004SRick Macklem 	return (error);
7239ec7b004SRick Macklem }
7249ec7b004SRick Macklem 
7259ec7b004SRick Macklem /*
7269ec7b004SRick Macklem  * Help break down an mbuf chain by setting the first siz bytes contiguous
7279ec7b004SRick Macklem  * pointed to by returned val.
7289ec7b004SRick Macklem  * This is used by the macro NFSM_DISSECT for tough
7299ec7b004SRick Macklem  * cases.
7309ec7b004SRick Macklem  */
731b9cc3262SRyan Moeller void *
nfsm_dissct(struct nfsrv_descript * nd,int siz,int how)732d96b98a3SKenneth D. Merry nfsm_dissct(struct nfsrv_descript *nd, int siz, int how)
7339ec7b004SRick Macklem {
734ae070589SRick Macklem 	struct mbuf *mp2;
7359ec7b004SRick Macklem 	int siz2, xfer;
7369ec7b004SRick Macklem 	caddr_t p;
7379ec7b004SRick Macklem 	int left;
7389ec7b004SRick Macklem 	caddr_t retp;
7399ec7b004SRick Macklem 
7409ec7b004SRick Macklem 	retp = NULL;
7413973ef1dSRick Macklem 	left = mtod(nd->nd_md, caddr_t) + nd->nd_md->m_len - nd->nd_dpos;
7429ec7b004SRick Macklem 	while (left == 0) {
7433973ef1dSRick Macklem 		nd->nd_md = nd->nd_md->m_next;
7443973ef1dSRick Macklem 		if (nd->nd_md == NULL)
7453973ef1dSRick Macklem 			return (retp);
7463973ef1dSRick Macklem 		left = nd->nd_md->m_len;
7473973ef1dSRick Macklem 		nd->nd_dpos = mtod(nd->nd_md, caddr_t);
7489ec7b004SRick Macklem 	}
7499ec7b004SRick Macklem 	if (left >= siz) {
7509ec7b004SRick Macklem 		retp = nd->nd_dpos;
7519ec7b004SRick Macklem 		nd->nd_dpos += siz;
7523973ef1dSRick Macklem 	} else if (nd->nd_md->m_next == NULL) {
7533973ef1dSRick Macklem 		return (retp);
7549ec7b004SRick Macklem 	} else if (siz > ncl_mbuf_mhlen) {
7559ec7b004SRick Macklem 		panic("nfs S too big");
7569ec7b004SRick Macklem 	} else {
7578b43388cSZhenlei Huang 		MGET(mp2, how, MT_DATA);
758d96b98a3SKenneth D. Merry 		if (mp2 == NULL)
759d96b98a3SKenneth D. Merry 			return (NULL);
760c948a17aSRick Macklem 		mp2->m_next = nd->nd_md->m_next;
761c948a17aSRick Macklem 		nd->nd_md->m_next = mp2;
762c948a17aSRick Macklem 		nd->nd_md->m_len -= left;
7633973ef1dSRick Macklem 		nd->nd_md = mp2;
7643973ef1dSRick Macklem 		retp = p = mtod(mp2, caddr_t);
7653973ef1dSRick Macklem 		NFSBCOPY(nd->nd_dpos, p, left);	/* Copy what was left */
7669ec7b004SRick Macklem 		siz2 = siz - left;
7679ec7b004SRick Macklem 		p += left;
7683973ef1dSRick Macklem 		mp2 = mp2->m_next;
7699ec7b004SRick Macklem 		/* Loop around copying up the siz2 bytes */
7709ec7b004SRick Macklem 		while (siz2 > 0) {
7713973ef1dSRick Macklem 			if (mp2 == NULL)
7729ec7b004SRick Macklem 				return (NULL);
7733973ef1dSRick Macklem 			xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
7743973ef1dSRick Macklem 			if (xfer > 0) {
7753973ef1dSRick Macklem 				NFSBCOPY(mtod(mp2, caddr_t), p, xfer);
7763973ef1dSRick Macklem 				mp2->m_data += xfer;
7773973ef1dSRick Macklem 				mp2->m_len -= xfer;
7789ec7b004SRick Macklem 				p += xfer;
7799ec7b004SRick Macklem 				siz2 -= xfer;
7809ec7b004SRick Macklem 			}
7813973ef1dSRick Macklem 			if (siz2 > 0)
7823973ef1dSRick Macklem 				mp2 = mp2->m_next;
7833973ef1dSRick Macklem 		}
7843973ef1dSRick Macklem 		nd->nd_md->m_len = siz;
7853973ef1dSRick Macklem 		nd->nd_md = mp2;
7863973ef1dSRick Macklem 		nd->nd_dpos = mtod(mp2, caddr_t);
7879ec7b004SRick Macklem 	}
7889ec7b004SRick Macklem 	return (retp);
7899ec7b004SRick Macklem }
7909ec7b004SRick Macklem 
7919ec7b004SRick Macklem /*
7929ec7b004SRick Macklem  * Advance the position in the mbuf chain.
7939ec7b004SRick Macklem  * If offs == 0, this is a no-op, but it is simpler to just return from
7949ec7b004SRick Macklem  * here than check for offs > 0 for all calls to nfsm_advance.
7959ec7b004SRick Macklem  * If left == -1, it should be calculated here.
7969ec7b004SRick Macklem  */
797b9cc3262SRyan Moeller int
nfsm_advance(struct nfsrv_descript * nd,int offs,int left)7989ec7b004SRick Macklem nfsm_advance(struct nfsrv_descript *nd, int offs, int left)
7999ec7b004SRick Macklem {
800a9285ae5SZack Kirsch 	int error = 0;
8019ec7b004SRick Macklem 
8029ec7b004SRick Macklem 	if (offs == 0)
803a9285ae5SZack Kirsch 		goto out;
8049ec7b004SRick Macklem 	/*
805778f2983SRick Macklem 	 * A negative offs might indicate a corrupted mbuf chain and,
806778f2983SRick Macklem 	 * as such, a printf is logged.
8079ec7b004SRick Macklem 	 */
808778f2983SRick Macklem 	if (offs < 0) {
809778f2983SRick Macklem 		printf("nfsrv_advance: negative offs\n");
810778f2983SRick Macklem 		error = EBADRPC;
811778f2983SRick Macklem 		goto out;
812778f2983SRick Macklem 	}
8139ec7b004SRick Macklem 
8149ec7b004SRick Macklem 	/*
8159ec7b004SRick Macklem 	 * If left == -1, calculate it here.
8169ec7b004SRick Macklem 	 */
8179ec7b004SRick Macklem 	if (left == -1)
818c948a17aSRick Macklem 		left = mtod(nd->nd_md, caddr_t) + nd->nd_md->m_len -
8199ec7b004SRick Macklem 		    nd->nd_dpos;
8209ec7b004SRick Macklem 
8219ec7b004SRick Macklem 	/*
8229ec7b004SRick Macklem 	 * Loop around, advancing over the mbuf data.
8239ec7b004SRick Macklem 	 */
8249ec7b004SRick Macklem 	while (offs > left) {
8259ec7b004SRick Macklem 		offs -= left;
826c948a17aSRick Macklem 		nd->nd_md = nd->nd_md->m_next;
827a9285ae5SZack Kirsch 		if (nd->nd_md == NULL) {
828a9285ae5SZack Kirsch 			error = EBADRPC;
829a9285ae5SZack Kirsch 			goto out;
830a9285ae5SZack Kirsch 		}
831c948a17aSRick Macklem 		left = nd->nd_md->m_len;
832c948a17aSRick Macklem 		nd->nd_dpos = mtod(nd->nd_md, caddr_t);
8339ec7b004SRick Macklem 	}
8349ec7b004SRick Macklem 	nd->nd_dpos += offs;
835a9285ae5SZack Kirsch 
836a9285ae5SZack Kirsch out:
837a9285ae5SZack Kirsch 	NFSEXITCODE(error);
838a9285ae5SZack Kirsch 	return (error);
8399ec7b004SRick Macklem }
8409ec7b004SRick Macklem 
8419ec7b004SRick Macklem /*
8429ec7b004SRick Macklem  * Copy a string into mbuf(s).
8439ec7b004SRick Macklem  * Return the number of bytes output, including XDR overheads.
8449ec7b004SRick Macklem  */
845b9cc3262SRyan Moeller int
nfsm_strtom(struct nfsrv_descript * nd,const char * cp,int siz)8469ec7b004SRick Macklem nfsm_strtom(struct nfsrv_descript *nd, const char *cp, int siz)
8479ec7b004SRick Macklem {
848ae070589SRick Macklem 	struct mbuf *m2;
8499ec7b004SRick Macklem 	int xfer, left;
850ae070589SRick Macklem 	struct mbuf *m1;
8519ec7b004SRick Macklem 	int rem, bytesize;
8529ec7b004SRick Macklem 	u_int32_t *tl;
8539ec7b004SRick Macklem 	char *cp2;
8549ec7b004SRick Macklem 
8559ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
8569ec7b004SRick Macklem 	*tl = txdr_unsigned(siz);
8579ec7b004SRick Macklem 	rem = NFSM_RNDUP(siz) - siz;
8589ec7b004SRick Macklem 	bytesize = NFSX_UNSIGNED + siz + rem;
8599ec7b004SRick Macklem 	m2 = nd->nd_mb;
8609ec7b004SRick Macklem 	cp2 = nd->nd_bpos;
86134fc29e0SRick Macklem 	if ((nd->nd_flag & ND_EXTPG) != 0)
86234fc29e0SRick Macklem 		left = nd->nd_bextpgsiz;
86334fc29e0SRick Macklem 	else
8649ec7b004SRick Macklem 		left = M_TRAILINGSPACE(m2);
8659ec7b004SRick Macklem 
86634fc29e0SRick Macklem 	KASSERT(((m2->m_flags & (M_EXT | M_EXTPG)) ==
86734fc29e0SRick Macklem 	    (M_EXT | M_EXTPG) && (nd->nd_flag & ND_EXTPG) != 0) ||
86834fc29e0SRick Macklem 	    ((m2->m_flags & (M_EXT | M_EXTPG)) !=
86934fc29e0SRick Macklem 	    (M_EXT | M_EXTPG) && (nd->nd_flag & ND_EXTPG) == 0),
87034fc29e0SRick Macklem 	    ("nfsm_strtom: ext_pgs and non-ext_pgs mbufs mixed"));
8719ec7b004SRick Macklem 	/*
8729ec7b004SRick Macklem 	 * Loop around copying the string to mbuf(s).
8739ec7b004SRick Macklem 	 */
8749ec7b004SRick Macklem 	while (siz > 0) {
8759ec7b004SRick Macklem 		if (left == 0) {
87634fc29e0SRick Macklem 			if ((nd->nd_flag & ND_EXTPG) != 0) {
87734fc29e0SRick Macklem 				m2 = nfsm_add_ext_pgs(m2,
87834fc29e0SRick Macklem 				    nd->nd_maxextsiz, &nd->nd_bextpg);
87934fc29e0SRick Macklem 				cp2 = (char *)(void *)PHYS_TO_DMAP(
88034fc29e0SRick Macklem 				    m2->m_epg_pa[nd->nd_bextpg]);
88134fc29e0SRick Macklem 				nd->nd_bextpgsiz = left = PAGE_SIZE;
88234fc29e0SRick Macklem 			} else {
8839ec7b004SRick Macklem 				if (siz > ncl_mbuf_mlen)
884eb1b1807SGleb Smirnoff 					NFSMCLGET(m1, M_WAITOK);
8859ec7b004SRick Macklem 				else
8869ec7b004SRick Macklem 					NFSMGET(m1);
887c948a17aSRick Macklem 				m1->m_len = 0;
88834fc29e0SRick Macklem 				cp2 = mtod(m1, char *);
88934fc29e0SRick Macklem 				left = M_TRAILINGSPACE(m1);
890c948a17aSRick Macklem 				m2->m_next = m1;
8919ec7b004SRick Macklem 				m2 = m1;
89234fc29e0SRick Macklem 			}
8939ec7b004SRick Macklem 		}
8949ec7b004SRick Macklem 		if (left >= siz)
8959ec7b004SRick Macklem 			xfer = siz;
8969ec7b004SRick Macklem 		else
8979ec7b004SRick Macklem 			xfer = left;
8989ec7b004SRick Macklem 		NFSBCOPY(cp, cp2, xfer);
8999ec7b004SRick Macklem 		cp += xfer;
90034fc29e0SRick Macklem 		cp2 += xfer;
901c948a17aSRick Macklem 		m2->m_len += xfer;
9029ec7b004SRick Macklem 		siz -= xfer;
9039ec7b004SRick Macklem 		left -= xfer;
90434fc29e0SRick Macklem 		if ((nd->nd_flag & ND_EXTPG) != 0) {
90534fc29e0SRick Macklem 			nd->nd_bextpgsiz -= xfer;
90634fc29e0SRick Macklem 			m2->m_epg_last_len += xfer;
90734fc29e0SRick Macklem 		}
9089ec7b004SRick Macklem 		if (siz == 0 && rem) {
9099ec7b004SRick Macklem 			if (left < rem)
9109ec7b004SRick Macklem 				panic("nfsm_strtom");
91134fc29e0SRick Macklem 			NFSBZERO(cp2, rem);
912c948a17aSRick Macklem 			m2->m_len += rem;
91334fc29e0SRick Macklem 			cp2 += rem;
91434fc29e0SRick Macklem 			if ((nd->nd_flag & ND_EXTPG) != 0) {
91534fc29e0SRick Macklem 				nd->nd_bextpgsiz -= rem;
91634fc29e0SRick Macklem 				m2->m_epg_last_len += rem;
91734fc29e0SRick Macklem 			}
9189ec7b004SRick Macklem 		}
9199ec7b004SRick Macklem 	}
9209ec7b004SRick Macklem 	nd->nd_mb = m2;
92134fc29e0SRick Macklem 	if ((nd->nd_flag & ND_EXTPG) != 0)
92234fc29e0SRick Macklem 		nd->nd_bpos = cp2;
92334fc29e0SRick Macklem 	else
92434fc29e0SRick Macklem 		nd->nd_bpos = mtod(m2, char *) + m2->m_len;
9259ec7b004SRick Macklem 	return (bytesize);
9269ec7b004SRick Macklem }
9279ec7b004SRick Macklem 
9289ec7b004SRick Macklem /*
9299ec7b004SRick Macklem  * Called once to initialize data structures...
9309ec7b004SRick Macklem  */
931b9cc3262SRyan Moeller void
newnfs_init(void)9329ec7b004SRick Macklem newnfs_init(void)
9339ec7b004SRick Macklem {
9349ec7b004SRick Macklem 	static int nfs_inited = 0;
9359ec7b004SRick Macklem 
9369ec7b004SRick Macklem 	if (nfs_inited)
9379ec7b004SRick Macklem 		return;
9389ec7b004SRick Macklem 	nfs_inited = 1;
9399ec7b004SRick Macklem 
9409ec7b004SRick Macklem 	newnfs_true = txdr_unsigned(TRUE);
9419ec7b004SRick Macklem 	newnfs_false = txdr_unsigned(FALSE);
9429ec7b004SRick Macklem 	newnfs_xdrneg1 = txdr_unsigned(-1);
9439ec7b004SRick Macklem 	nfscl_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
9449ec7b004SRick Macklem 	if (nfscl_ticks < 1)
9459ec7b004SRick Macklem 		nfscl_ticks = 1;
9469ec7b004SRick Macklem 	NFSSETBOOTTIME(nfsboottime);
9479ec7b004SRick Macklem 
9489ec7b004SRick Macklem 	/*
9499ec7b004SRick Macklem 	 * Initialize reply list and start timer
9509ec7b004SRick Macklem 	 */
9519ec7b004SRick Macklem 	TAILQ_INIT(&nfsd_reqq);
9529ec7b004SRick Macklem }
9539ec7b004SRick Macklem 
9549ec7b004SRick Macklem /*
9559ec7b004SRick Macklem  * Put a file handle in an mbuf list.
9569ec7b004SRick Macklem  * If the size argument == 0, just use the default size.
9579ec7b004SRick Macklem  * set_true == 1 if there should be an newnfs_true prepended on the file handle.
9589ec7b004SRick Macklem  * Return the number of bytes output, including XDR overhead.
9599ec7b004SRick Macklem  */
960b9cc3262SRyan Moeller int
nfsm_fhtom(struct nfsmount * nmp,struct nfsrv_descript * nd,u_int8_t * fhp,int size,int set_true)961896516e5SRick Macklem nfsm_fhtom(struct nfsmount *nmp, struct nfsrv_descript *nd, u_int8_t *fhp,
962896516e5SRick Macklem     int size, int set_true)
9639ec7b004SRick Macklem {
9649ec7b004SRick Macklem 	u_int32_t *tl;
9659ec7b004SRick Macklem 	u_int8_t *cp;
966638b90a1SRick Macklem 	int fullsiz, bytesize = 0;
9679ec7b004SRick Macklem 
968896516e5SRick Macklem 	KASSERT(nmp == NULL || nmp->nm_fhsize > 0,
969896516e5SRick Macklem 	    ("nfsm_fhtom: 0 length fh"));
9709ec7b004SRick Macklem 	if (size == 0)
9719ec7b004SRick Macklem 		size = NFSX_MYFH;
9729ec7b004SRick Macklem 	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
9739ec7b004SRick Macklem 	case ND_NFSV2:
9749ec7b004SRick Macklem 		if (size > NFSX_V2FH)
9759ec7b004SRick Macklem 			panic("fh size > NFSX_V2FH for NFSv2");
9769ec7b004SRick Macklem 		NFSM_BUILD(cp, u_int8_t *, NFSX_V2FH);
9779ec7b004SRick Macklem 		NFSBCOPY(fhp, cp, size);
9789ec7b004SRick Macklem 		if (size < NFSX_V2FH)
9799ec7b004SRick Macklem 			NFSBZERO(cp + size, NFSX_V2FH - size);
9809ec7b004SRick Macklem 		bytesize = NFSX_V2FH;
9819ec7b004SRick Macklem 		break;
9829ec7b004SRick Macklem 	case ND_NFSV3:
9839ec7b004SRick Macklem 	case ND_NFSV4:
984896516e5SRick Macklem 		if (size == NFSX_FHMAX + 1 && nmp != NULL &&
985896516e5SRick Macklem 		    (nmp->nm_privflag & NFSMNTP_FAKEROOTFH) != 0) {
986896516e5SRick Macklem 			fhp = nmp->nm_fh;
987896516e5SRick Macklem 			size = nmp->nm_fhsize;
988896516e5SRick Macklem 		}
9899ec7b004SRick Macklem 		fullsiz = NFSM_RNDUP(size);
9909ec7b004SRick Macklem 		if (set_true) {
9919ec7b004SRick Macklem 		    bytesize = 2 * NFSX_UNSIGNED + fullsiz;
9929ec7b004SRick Macklem 		    NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
9939ec7b004SRick Macklem 		    *tl = newnfs_true;
9949ec7b004SRick Macklem 		} else {
9959ec7b004SRick Macklem 		    bytesize = NFSX_UNSIGNED + fullsiz;
9969ec7b004SRick Macklem 		}
9979ec7b004SRick Macklem 		(void) nfsm_strtom(nd, fhp, size);
9989ec7b004SRick Macklem 		break;
99974b8d63dSPedro F. Giffuni 	}
10009ec7b004SRick Macklem 	return (bytesize);
10019ec7b004SRick Macklem }
10029ec7b004SRick Macklem 
10039ec7b004SRick Macklem /*
10049ec7b004SRick Macklem  * This function compares two net addresses by family and returns TRUE
10059ec7b004SRick Macklem  * if they are the same host.
10069ec7b004SRick Macklem  * If there is any doubt, return FALSE.
10079ec7b004SRick Macklem  * The AF_INET family is handled as a special case so that address mbufs
10089ec7b004SRick Macklem  * don't need to be saved to store "struct in_addr", which is only 4 bytes.
10099ec7b004SRick Macklem  */
1010b9cc3262SRyan Moeller int
nfsaddr_match(int family,union nethostaddr * haddr,NFSSOCKADDR_T nam)10119ec7b004SRick Macklem nfsaddr_match(int family, union nethostaddr *haddr, NFSSOCKADDR_T nam)
10129ec7b004SRick Macklem {
1013a6f77c9aSRick Macklem #ifdef INET
10149ec7b004SRick Macklem 	struct sockaddr_in *inetaddr;
1015a6f77c9aSRick Macklem #endif
10169ec7b004SRick Macklem 
10179ec7b004SRick Macklem 	switch (family) {
1018a6f77c9aSRick Macklem #ifdef INET
10199ec7b004SRick Macklem 	case AF_INET:
10209ec7b004SRick Macklem 		inetaddr = NFSSOCKADDR(nam, struct sockaddr_in *);
10219ec7b004SRick Macklem 		if (inetaddr->sin_family == AF_INET &&
10229ec7b004SRick Macklem 		    inetaddr->sin_addr.s_addr == haddr->had_inet.s_addr)
10239ec7b004SRick Macklem 			return (1);
10249ec7b004SRick Macklem 		break;
1025a6f77c9aSRick Macklem #endif
10269ec7b004SRick Macklem #ifdef INET6
10279ec7b004SRick Macklem 	case AF_INET6:
10289ec7b004SRick Macklem 		{
10299ec7b004SRick Macklem 		struct sockaddr_in6 *inetaddr6;
10309ec7b004SRick Macklem 
10319ec7b004SRick Macklem 		inetaddr6 = NFSSOCKADDR(nam, struct sockaddr_in6 *);
10329ec7b004SRick Macklem 		/* XXX - should test sin6_scope_id ? */
10339ec7b004SRick Macklem 		if (inetaddr6->sin6_family == AF_INET6 &&
10349ec7b004SRick Macklem 		    IN6_ARE_ADDR_EQUAL(&inetaddr6->sin6_addr,
10359ec7b004SRick Macklem 			  &haddr->had_inet6))
10369ec7b004SRick Macklem 			return (1);
10379ec7b004SRick Macklem 		}
10389ec7b004SRick Macklem 		break;
10399ec7b004SRick Macklem #endif
104074b8d63dSPedro F. Giffuni 	}
10419ec7b004SRick Macklem 	return (0);
10429ec7b004SRick Macklem }
10439ec7b004SRick Macklem 
10449ec7b004SRick Macklem /*
10459ec7b004SRick Macklem  * Similar to the above, but takes to NFSSOCKADDR_T args.
10469ec7b004SRick Macklem  */
1047b9cc3262SRyan Moeller int
nfsaddr2_match(NFSSOCKADDR_T nam1,NFSSOCKADDR_T nam2)10489ec7b004SRick Macklem nfsaddr2_match(NFSSOCKADDR_T nam1, NFSSOCKADDR_T nam2)
10499ec7b004SRick Macklem {
10509ec7b004SRick Macklem 	struct sockaddr_in *addr1, *addr2;
10519ec7b004SRick Macklem 	struct sockaddr *inaddr;
10529ec7b004SRick Macklem 
10539ec7b004SRick Macklem 	inaddr = NFSSOCKADDR(nam1, struct sockaddr *);
10549ec7b004SRick Macklem 	switch (inaddr->sa_family) {
10559ec7b004SRick Macklem 	case AF_INET:
10569ec7b004SRick Macklem 		addr1 = NFSSOCKADDR(nam1, struct sockaddr_in *);
10579ec7b004SRick Macklem 		addr2 = NFSSOCKADDR(nam2, struct sockaddr_in *);
10589ec7b004SRick Macklem 		if (addr2->sin_family == AF_INET &&
10599ec7b004SRick Macklem 		    addr1->sin_addr.s_addr == addr2->sin_addr.s_addr)
10609ec7b004SRick Macklem 			return (1);
10619ec7b004SRick Macklem 		break;
10629ec7b004SRick Macklem #ifdef INET6
10639ec7b004SRick Macklem 	case AF_INET6:
10649ec7b004SRick Macklem 		{
10659ec7b004SRick Macklem 		struct sockaddr_in6 *inet6addr1, *inet6addr2;
10669ec7b004SRick Macklem 
10679ec7b004SRick Macklem 		inet6addr1 = NFSSOCKADDR(nam1, struct sockaddr_in6 *);
10689ec7b004SRick Macklem 		inet6addr2 = NFSSOCKADDR(nam2, struct sockaddr_in6 *);
10699ec7b004SRick Macklem 		/* XXX - should test sin6_scope_id ? */
10709ec7b004SRick Macklem 		if (inet6addr2->sin6_family == AF_INET6 &&
10719ec7b004SRick Macklem 		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
10729ec7b004SRick Macklem 			  &inet6addr2->sin6_addr))
10739ec7b004SRick Macklem 			return (1);
10749ec7b004SRick Macklem 		}
10759ec7b004SRick Macklem 		break;
10769ec7b004SRick Macklem #endif
107774b8d63dSPedro F. Giffuni 	}
10789ec7b004SRick Macklem 	return (0);
10799ec7b004SRick Macklem }
10809ec7b004SRick Macklem 
10819ec7b004SRick Macklem /*
10829ec7b004SRick Macklem  * Dissect a file handle on the client.
10839ec7b004SRick Macklem  */
1084b9cc3262SRyan Moeller int
nfsm_getfh(struct nfsrv_descript * nd,struct nfsfh ** nfhpp)10859ec7b004SRick Macklem nfsm_getfh(struct nfsrv_descript *nd, struct nfsfh **nfhpp)
10869ec7b004SRick Macklem {
10879ec7b004SRick Macklem 	u_int32_t *tl;
10889ec7b004SRick Macklem 	struct nfsfh *nfhp;
10899ec7b004SRick Macklem 	int error, len;
10909ec7b004SRick Macklem 
10919ec7b004SRick Macklem 	*nfhpp = NULL;
10929ec7b004SRick Macklem 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
10939ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
10949ec7b004SRick Macklem 		if ((len = fxdr_unsigned(int, *tl)) <= 0 ||
1095a9285ae5SZack Kirsch 			len > NFSX_FHMAX) {
1096a9285ae5SZack Kirsch 			error = EBADRPC;
1097a9285ae5SZack Kirsch 			goto nfsmout;
1098a9285ae5SZack Kirsch 		}
10999ec7b004SRick Macklem 	} else
11009ec7b004SRick Macklem 		len = NFSX_V2FH;
1101222daa42SConrad Meyer 	nfhp = malloc(sizeof (struct nfsfh) + len,
11029ec7b004SRick Macklem 	    M_NFSFH, M_WAITOK);
11039ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, nfhp->nfh_fh, len);
11049ec7b004SRick Macklem 	if (error) {
1105222daa42SConrad Meyer 		free(nfhp, M_NFSFH);
1106a9285ae5SZack Kirsch 		goto nfsmout;
11079ec7b004SRick Macklem 	}
11089ec7b004SRick Macklem 	nfhp->nfh_len = len;
11099ec7b004SRick Macklem 	*nfhpp = nfhp;
11109ec7b004SRick Macklem nfsmout:
1111a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
11129ec7b004SRick Macklem 	return (error);
11139ec7b004SRick Macklem }
11149ec7b004SRick Macklem 
11159ec7b004SRick Macklem /*
11169ec7b004SRick Macklem  * Break down the nfsv4 acl.
11179ec7b004SRick Macklem  * If the aclp == NULL or won't fit in an acl, just discard the acl info.
11189ec7b004SRick Macklem  */
1119b9cc3262SRyan Moeller int
nfsrv_dissectacl(struct nfsrv_descript * nd,NFSACL_T * aclp,bool server,int * aclerrp,int * aclsizep,__unused NFSPROC_T * p)1120a91a5784SRick Macklem nfsrv_dissectacl(struct nfsrv_descript *nd, NFSACL_T *aclp, bool server,
1121a91a5784SRick Macklem     int *aclerrp, int *aclsizep, __unused NFSPROC_T *p)
11229ec7b004SRick Macklem {
11239ec7b004SRick Macklem 	u_int32_t *tl;
11249ec7b004SRick Macklem 	int i, aclsize;
11259ec7b004SRick Macklem 	int acecnt, error = 0, aceerr = 0, acesize;
11269ec7b004SRick Macklem 
11279ec7b004SRick Macklem 	*aclerrp = 0;
11289ec7b004SRick Macklem 	if (aclp)
11299ec7b004SRick Macklem 		aclp->acl_cnt = 0;
11309ec7b004SRick Macklem 	/*
11319ec7b004SRick Macklem 	 * Parse out the ace entries and expect them to conform to
11329ec7b004SRick Macklem 	 * what can be supported by R/W/X bits.
11339ec7b004SRick Macklem 	 */
11349ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
11359ec7b004SRick Macklem 	aclsize = NFSX_UNSIGNED;
11369ec7b004SRick Macklem 	acecnt = fxdr_unsigned(int, *tl);
1137db0ac6deSCy Schubert 	/*
1138db0ac6deSCy Schubert 	 * The RFCs do not define a fixed limit to the number of ACEs in
1139db0ac6deSCy Schubert 	 * an ACL, but 10240 should be more than sufficient.
1140db0ac6deSCy Schubert 	 */
1141db0ac6deSCy Schubert 	if (acecnt < 0 || acecnt > 10240) {
1142db0ac6deSCy Schubert 		error = NFSERR_BADXDR;
1143db0ac6deSCy Schubert 		goto nfsmout;
1144db0ac6deSCy Schubert 	}
11459ec7b004SRick Macklem 	if (acecnt > ACL_MAX_ENTRIES)
1146b008a72cSZack Kirsch 		aceerr = NFSERR_ATTRNOTSUPP;
11479ec7b004SRick Macklem 	if (nfsrv_useacl == 0)
1148b008a72cSZack Kirsch 		aceerr = NFSERR_ATTRNOTSUPP;
11499ec7b004SRick Macklem 	for (i = 0; i < acecnt; i++) {
11509ec7b004SRick Macklem 		if (aclp && !aceerr)
11519ec7b004SRick Macklem 			error = nfsrv_dissectace(nd, &aclp->acl_entry[i],
1152a91a5784SRick Macklem 			    server, &aceerr, &acesize, p);
11539ec7b004SRick Macklem 		else
11549ec7b004SRick Macklem 			error = nfsrv_skipace(nd, &acesize);
11559ec7b004SRick Macklem 		if (error)
1156a9285ae5SZack Kirsch 			goto nfsmout;
11579ec7b004SRick Macklem 		aclsize += acesize;
11589ec7b004SRick Macklem 	}
11599ec7b004SRick Macklem 	if (aclp && !aceerr)
11609ec7b004SRick Macklem 		aclp->acl_cnt = acecnt;
11619ec7b004SRick Macklem 	if (aceerr)
11629ec7b004SRick Macklem 		*aclerrp = aceerr;
11639ec7b004SRick Macklem 	if (aclsizep)
11649ec7b004SRick Macklem 		*aclsizep = aclsize;
11659ec7b004SRick Macklem nfsmout:
1166a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
11679ec7b004SRick Macklem 	return (error);
11689ec7b004SRick Macklem }
11699ec7b004SRick Macklem 
11709ec7b004SRick Macklem /*
11719ec7b004SRick Macklem  * Skip over an NFSv4 ace entry. Just dissect the xdr and discard it.
11729ec7b004SRick Macklem  */
11739ec7b004SRick Macklem static int
nfsrv_skipace(struct nfsrv_descript * nd,int * acesizep)11749ec7b004SRick Macklem nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep)
11759ec7b004SRick Macklem {
11769ec7b004SRick Macklem 	u_int32_t *tl;
11779ec7b004SRick Macklem 	int error, len = 0;
11789ec7b004SRick Macklem 
11799ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
11809ec7b004SRick Macklem 	len = fxdr_unsigned(int, *(tl + 3));
11819ec7b004SRick Macklem 	error = nfsm_advance(nd, NFSM_RNDUP(len), -1);
11829ec7b004SRick Macklem nfsmout:
11839ec7b004SRick Macklem 	*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
1184a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
11859ec7b004SRick Macklem 	return (error);
11869ec7b004SRick Macklem }
11879ec7b004SRick Macklem 
11889ec7b004SRick Macklem /*
11899ec7b004SRick Macklem  * Get attribute bits from an mbuf list.
11909ec7b004SRick Macklem  * Returns EBADRPC for a parsing error, 0 otherwise.
11919ec7b004SRick Macklem  * If the clearinvalid flag is set, clear the bits not supported.
11929ec7b004SRick Macklem  */
1193b9cc3262SRyan Moeller int
nfsrv_getattrbits(struct nfsrv_descript * nd,nfsattrbit_t * attrbitp,int * cntp,int * retnotsupp)11949ec7b004SRick Macklem nfsrv_getattrbits(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp, int *cntp,
11959ec7b004SRick Macklem     int *retnotsupp)
11969ec7b004SRick Macklem {
11979ec7b004SRick Macklem 	u_int32_t *tl;
11989ec7b004SRick Macklem 	int cnt, i, outcnt;
11999ec7b004SRick Macklem 	int error = 0;
12009ec7b004SRick Macklem 
12019ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
12029ec7b004SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
1203a9285ae5SZack Kirsch 	if (cnt < 0) {
1204a9285ae5SZack Kirsch 		error = NFSERR_BADXDR;
1205a9285ae5SZack Kirsch 		goto nfsmout;
1206a9285ae5SZack Kirsch 	}
1207a36b76a7SRick Macklem 	if (cnt > NFSATTRBIT_MAXWORDS)
12089ec7b004SRick Macklem 		outcnt = NFSATTRBIT_MAXWORDS;
1209a36b76a7SRick Macklem 	else
12109ec7b004SRick Macklem 		outcnt = cnt;
12119ec7b004SRick Macklem 	NFSZERO_ATTRBIT(attrbitp);
12129ec7b004SRick Macklem 	if (outcnt > 0) {
12139ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, outcnt * NFSX_UNSIGNED);
12149ec7b004SRick Macklem 		for (i = 0; i < outcnt; i++)
12159ec7b004SRick Macklem 			attrbitp->bits[i] = fxdr_unsigned(u_int32_t, *tl++);
12169ec7b004SRick Macklem 	}
1217a36b76a7SRick Macklem 	for (i = 0; i < (cnt - outcnt); i++) {
1218a36b76a7SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
1219a36b76a7SRick Macklem 		if (retnotsupp != NULL && *tl != 0)
1220a36b76a7SRick Macklem 			*retnotsupp = NFSERR_ATTRNOTSUPP;
1221a36b76a7SRick Macklem 	}
12229ec7b004SRick Macklem 	if (cntp)
12239ec7b004SRick Macklem 		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
12249ec7b004SRick Macklem nfsmout:
1225a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
12269ec7b004SRick Macklem 	return (error);
12279ec7b004SRick Macklem }
12289ec7b004SRick Macklem 
12299ec7b004SRick Macklem /*
1230f4179ad4SRick Macklem  * Get operation bits from an mbuf list.
1231f4179ad4SRick Macklem  * Returns EBADRPC for a parsing error, 0 otherwise.
1232f4179ad4SRick Macklem  */
1233f4179ad4SRick Macklem int
nfsrv_getopbits(struct nfsrv_descript * nd,nfsopbit_t * opbitp,int * cntp)1234f4179ad4SRick Macklem nfsrv_getopbits(struct nfsrv_descript *nd, nfsopbit_t *opbitp, int *cntp)
1235f4179ad4SRick Macklem {
1236f4179ad4SRick Macklem 	uint32_t *tl;
1237f4179ad4SRick Macklem 	int cnt, i, outcnt;
1238f4179ad4SRick Macklem 	int error = 0;
1239f4179ad4SRick Macklem 
1240f4179ad4SRick Macklem 	NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
1241f4179ad4SRick Macklem 	cnt = fxdr_unsigned(int, *tl);
1242f4179ad4SRick Macklem 	if (cnt < 0) {
1243f4179ad4SRick Macklem 		error = NFSERR_BADXDR;
1244f4179ad4SRick Macklem 		goto nfsmout;
1245f4179ad4SRick Macklem 	}
1246f4179ad4SRick Macklem 	if (cnt > NFSOPBIT_MAXWORDS)
1247f4179ad4SRick Macklem 		outcnt = NFSOPBIT_MAXWORDS;
1248f4179ad4SRick Macklem 	else
1249f4179ad4SRick Macklem 		outcnt = cnt;
1250f4179ad4SRick Macklem 	NFSZERO_OPBIT(opbitp);
1251f4179ad4SRick Macklem 	if (outcnt > 0) {
1252f4179ad4SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, outcnt * NFSX_UNSIGNED);
1253f4179ad4SRick Macklem 		for (i = 0; i < outcnt; i++)
1254f4179ad4SRick Macklem 			opbitp->bits[i] = fxdr_unsigned(uint32_t, *tl++);
1255f4179ad4SRick Macklem 	}
1256f4179ad4SRick Macklem 	for (i = 0; i < (cnt - outcnt); i++) {
1257f4179ad4SRick Macklem 		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
1258f4179ad4SRick Macklem 		if (*tl != 0) {
1259f4179ad4SRick Macklem 			error = NFSERR_BADXDR;
1260f4179ad4SRick Macklem 			goto nfsmout;
1261f4179ad4SRick Macklem 		}
1262f4179ad4SRick Macklem 	}
1263f4179ad4SRick Macklem 	if (cntp != NULL)
1264f4179ad4SRick Macklem 		*cntp = NFSX_UNSIGNED + (cnt * NFSX_UNSIGNED);
1265f4179ad4SRick Macklem nfsmout:
1266f4179ad4SRick Macklem 	NFSEXITCODE2(error, nd);
1267f4179ad4SRick Macklem 	return (error);
1268f4179ad4SRick Macklem }
1269f4179ad4SRick Macklem 
1270f4179ad4SRick Macklem /*
12719ec7b004SRick Macklem  * Get the attributes for V4.
12729ec7b004SRick Macklem  * If the compare flag is true, test for any attribute changes,
12739ec7b004SRick Macklem  * otherwise return the attribute values.
12749ec7b004SRick Macklem  * These attributes cover fields in "struct vattr", "struct statfs",
12759ec7b004SRick Macklem  * "struct nfsfsinfo", the file handle and the lease duration.
12769ec7b004SRick Macklem  * The value of retcmpp is set to 1 if all attributes are the same,
12779ec7b004SRick Macklem  * and 0 otherwise.
12789ec7b004SRick Macklem  * Returns EBADRPC if it can't be parsed, 0 otherwise.
12799ec7b004SRick Macklem  */
1280b9cc3262SRyan Moeller int
nfsv4_loadattr(struct nfsrv_descript * nd,vnode_t vp,struct nfsvattr * nap,struct nfsfh ** nfhpp,fhandle_t * fhp,int fhsize,struct nfsv3_pathconf * pc,struct statfs * sbp,struct nfsstatfs * sfp,struct nfsfsinfo * fsp,NFSACL_T * aclp,int compare,int * retcmpp,u_int32_t * leasep,u_int32_t * rderrp,NFSPROC_T * p,struct ucred * cred)12819ec7b004SRick Macklem nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
12829ec7b004SRick Macklem     struct nfsvattr *nap, struct nfsfh **nfhpp, fhandle_t *fhp, int fhsize,
12839ec7b004SRick Macklem     struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
12849ec7b004SRick Macklem     struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
12859ec7b004SRick Macklem     u_int32_t *leasep, u_int32_t *rderrp, NFSPROC_T *p, struct ucred *cred)
12869ec7b004SRick Macklem {
12879ec7b004SRick Macklem 	u_int32_t *tl;
1288a9285ae5SZack Kirsch 	int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
12899ec7b004SRick Macklem 	int error, tfhsize, aceerr, attrsize, cnt, retnotsup;
12909ec7b004SRick Macklem 	u_char *cp, *cp2, namestr[NFSV4_SMALLSTR + 1];
12919ec7b004SRick Macklem 	nfsattrbit_t attrbits, retattrbits, checkattrbits;
12929ec7b004SRick Macklem 	struct nfsfh *tnfhp;
12939ec7b004SRick Macklem 	struct nfsreferral *refp;
12949ec7b004SRick Macklem 	u_quad_t tquad;
12959ec7b004SRick Macklem 	nfsquad_t tnfsquad;
12969ec7b004SRick Macklem 	struct timespec temptime;
12979ec7b004SRick Macklem 	uid_t uid;
12989ec7b004SRick Macklem 	gid_t gid;
12999ec7b004SRick Macklem 	u_int32_t freenum = 0, tuint;
13009ec7b004SRick Macklem 	u_int64_t uquad = 0, thyp, thyp2;
13019ec7b004SRick Macklem #ifdef QUOTA
13029ec7b004SRick Macklem 	struct dqblk dqb;
13039ec7b004SRick Macklem 	uid_t savuid;
13049ec7b004SRick Macklem #endif
130563659ba6SColin Percival 
130695ac7f1aSRick Macklem 	CTASSERT(sizeof(ino_t) == sizeof(uint64_t));
1307f0db2b60SRick Macklem 	NFSD_CURVNET_SET_QUIET(NFSD_TD_TO_VNET(curthread));
13089ec7b004SRick Macklem 	if (compare) {
13099ec7b004SRick Macklem 		retnotsup = 0;
13109ec7b004SRick Macklem 		error = nfsrv_getattrbits(nd, &attrbits, NULL, &retnotsup);
13119ec7b004SRick Macklem 	} else {
13129ec7b004SRick Macklem 		error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
13139ec7b004SRick Macklem 	}
13149ec7b004SRick Macklem 	if (error)
1315a9285ae5SZack Kirsch 		goto nfsmout;
13169ec7b004SRick Macklem 
13179ec7b004SRick Macklem 	if (compare) {
13189ec7b004SRick Macklem 		*retcmpp = retnotsup;
13199ec7b004SRick Macklem 	} else {
13209ec7b004SRick Macklem 		/*
13219ec7b004SRick Macklem 		 * Just set default values to some of the important ones.
13229ec7b004SRick Macklem 		 */
13239ec7b004SRick Macklem 		if (nap != NULL) {
132444328abfSAlan Somers 			VATTR_NULL(&nap->na_vattr);
13259ec7b004SRick Macklem 			nap->na_type = VREG;
13269ec7b004SRick Macklem 			nap->na_mode = 0;
13279ec7b004SRick Macklem 			nap->na_rdev = (NFSDEV_T)0;
13289ec7b004SRick Macklem 			nap->na_mtime.tv_sec = 0;
13299ec7b004SRick Macklem 			nap->na_mtime.tv_nsec = 0;
1330dd02d9d6SRick Macklem 			nap->na_btime.tv_sec = -1;
1331dd02d9d6SRick Macklem 			nap->na_btime.tv_nsec = 0;
13329ec7b004SRick Macklem 			nap->na_gen = 0;
13339ec7b004SRick Macklem 			nap->na_flags = 0;
13349ec7b004SRick Macklem 			nap->na_blocksize = NFS_FABLKSIZE;
13359ec7b004SRick Macklem 		}
13369ec7b004SRick Macklem 		if (sbp != NULL) {
13379ec7b004SRick Macklem 			sbp->f_bsize = NFS_FABLKSIZE;
13389ec7b004SRick Macklem 			sbp->f_blocks = 0;
13399ec7b004SRick Macklem 			sbp->f_bfree = 0;
13409ec7b004SRick Macklem 			sbp->f_bavail = 0;
13419ec7b004SRick Macklem 			sbp->f_files = 0;
13429ec7b004SRick Macklem 			sbp->f_ffree = 0;
13439ec7b004SRick Macklem 		}
13449ec7b004SRick Macklem 		if (fsp != NULL) {
13459ec7b004SRick Macklem 			fsp->fs_rtmax = 8192;
13469ec7b004SRick Macklem 			fsp->fs_rtpref = 8192;
13479ec7b004SRick Macklem 			fsp->fs_maxname = NFS_MAXNAMLEN;
13489ec7b004SRick Macklem 			fsp->fs_wtmax = 8192;
13499ec7b004SRick Macklem 			fsp->fs_wtpref = 8192;
13509ec7b004SRick Macklem 			fsp->fs_wtmult = NFS_FABLKSIZE;
13519ec7b004SRick Macklem 			fsp->fs_dtpref = 8192;
13529ec7b004SRick Macklem 			fsp->fs_maxfilesize = 0xffffffffffffffffull;
13539ec7b004SRick Macklem 			fsp->fs_timedelta.tv_sec = 0;
13549ec7b004SRick Macklem 			fsp->fs_timedelta.tv_nsec = 1;
13559ec7b004SRick Macklem 			fsp->fs_properties = (NFSV3_FSFLINK | NFSV3_FSFSYMLINK |
13569ec7b004SRick Macklem 				NFSV3_FSFHOMOGENEOUS | NFSV3_FSFCANSETTIME);
13579ec7b004SRick Macklem 		}
13589ec7b004SRick Macklem 		if (pc != NULL) {
1359a0a073b1SJohn Baldwin 			pc->pc_linkmax = NFS_LINK_MAX;
13609ec7b004SRick Macklem 			pc->pc_namemax = NAME_MAX;
13619ec7b004SRick Macklem 			pc->pc_notrunc = 0;
13629ec7b004SRick Macklem 			pc->pc_chownrestricted = 0;
13639ec7b004SRick Macklem 			pc->pc_caseinsensitive = 0;
13649ec7b004SRick Macklem 			pc->pc_casepreserving = 1;
13659ec7b004SRick Macklem 		}
1366fb556791SRick Macklem 		if (sfp != NULL) {
1367fb556791SRick Macklem 			sfp->sf_ffiles = UINT64_MAX;
1368fb556791SRick Macklem 			sfp->sf_tfiles = UINT64_MAX;
1369fb556791SRick Macklem 			sfp->sf_afiles = UINT64_MAX;
1370fb556791SRick Macklem 			sfp->sf_fbytes = UINT64_MAX;
1371fb556791SRick Macklem 			sfp->sf_tbytes = UINT64_MAX;
1372fb556791SRick Macklem 			sfp->sf_abytes = UINT64_MAX;
1373fb556791SRick Macklem 		}
13749ec7b004SRick Macklem 	}
13759ec7b004SRick Macklem 
13769ec7b004SRick Macklem 	/*
13779ec7b004SRick Macklem 	 * Loop around getting the attributes.
13789ec7b004SRick Macklem 	 */
13799ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
13809ec7b004SRick Macklem 	attrsize = fxdr_unsigned(int, *tl);
13819ec7b004SRick Macklem 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
13829ec7b004SRick Macklem 	    if (attrsum > attrsize) {
13839ec7b004SRick Macklem 		error = NFSERR_BADXDR;
13849ec7b004SRick Macklem 		goto nfsmout;
13859ec7b004SRick Macklem 	    }
13869ec7b004SRick Macklem 	    if (NFSISSET_ATTRBIT(&attrbits, bitpos))
13879ec7b004SRick Macklem 		switch (bitpos) {
13889ec7b004SRick Macklem 		case NFSATTRBIT_SUPPORTEDATTRS:
13899ec7b004SRick Macklem 			retnotsup = 0;
13909ec7b004SRick Macklem 			if (compare || nap == NULL)
13919ec7b004SRick Macklem 			    error = nfsrv_getattrbits(nd, &retattrbits,
13929ec7b004SRick Macklem 				&cnt, &retnotsup);
13939ec7b004SRick Macklem 			else
13949ec7b004SRick Macklem 			    error = nfsrv_getattrbits(nd, &nap->na_suppattr,
13959ec7b004SRick Macklem 				&cnt, &retnotsup);
13969ec7b004SRick Macklem 			if (error)
1397a9285ae5SZack Kirsch 			    goto nfsmout;
13989ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
1399ea5776ecSRick Macklem 			   NFSSETSUPP_ATTRBIT(&checkattrbits, nd);
1400d8a5961fSMarcelo Araujo 
1401d8a5961fSMarcelo Araujo 			   /* Some filesystem do not support NFSv4ACL   */
1402d8a5961fSMarcelo Araujo 			   if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
1403d8a5961fSMarcelo Araujo 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACL);
1404d8a5961fSMarcelo Araujo 				NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_ACLSUPPORT);
1405d8a5961fSMarcelo Araujo 		   	   }
14069ec7b004SRick Macklem 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
14079ec7b004SRick Macklem 			       || retnotsup)
14089ec7b004SRick Macklem 				*retcmpp = NFSERR_NOTSAME;
14099ec7b004SRick Macklem 			}
14109ec7b004SRick Macklem 			attrsum += cnt;
14119ec7b004SRick Macklem 			break;
14129ec7b004SRick Macklem 		case NFSATTRBIT_TYPE:
14139ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
14149ec7b004SRick Macklem 			if (compare) {
14159ec7b004SRick Macklem 				if (!(*retcmpp)) {
14169ec7b004SRick Macklem 				    if (nap->na_type != nfsv34tov_type(*tl))
14179ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
14189ec7b004SRick Macklem 				}
14199ec7b004SRick Macklem 			} else if (nap != NULL) {
14209ec7b004SRick Macklem 				nap->na_type = nfsv34tov_type(*tl);
14219ec7b004SRick Macklem 			}
14229ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
14239ec7b004SRick Macklem 			break;
14249ec7b004SRick Macklem 		case NFSATTRBIT_FHEXPIRETYPE:
14259ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
14269ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
14279ec7b004SRick Macklem 				if (fxdr_unsigned(int, *tl) !=
14289ec7b004SRick Macklem 					NFSV4FHTYPE_PERSISTENT)
14299ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
14309ec7b004SRick Macklem 			}
14319ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
14329ec7b004SRick Macklem 			break;
14339ec7b004SRick Macklem 		case NFSATTRBIT_CHANGE:
14349ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
14359ec7b004SRick Macklem 			if (compare) {
14369ec7b004SRick Macklem 				if (!(*retcmpp)) {
14379ec7b004SRick Macklem 				    if (nap->na_filerev != fxdr_hyper(tl))
14389ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
14399ec7b004SRick Macklem 				}
14409ec7b004SRick Macklem 			} else if (nap != NULL) {
14419ec7b004SRick Macklem 				nap->na_filerev = fxdr_hyper(tl);
14429ec7b004SRick Macklem 			}
14439ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
14449ec7b004SRick Macklem 			break;
14459ec7b004SRick Macklem 		case NFSATTRBIT_SIZE:
14469ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
14479ec7b004SRick Macklem 			if (compare) {
14489ec7b004SRick Macklem 				if (!(*retcmpp)) {
14499ec7b004SRick Macklem 				    if (nap->na_size != fxdr_hyper(tl))
14509ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
14519ec7b004SRick Macklem 				}
14529ec7b004SRick Macklem 			} else if (nap != NULL) {
14539ec7b004SRick Macklem 				nap->na_size = fxdr_hyper(tl);
14549ec7b004SRick Macklem 			}
14559ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
14569ec7b004SRick Macklem 			break;
14579ec7b004SRick Macklem 		case NFSATTRBIT_LINKSUPPORT:
14589ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
14599ec7b004SRick Macklem 			if (compare) {
14609ec7b004SRick Macklem 				if (!(*retcmpp)) {
14619ec7b004SRick Macklem 				    if (fsp->fs_properties & NFSV3_FSFLINK) {
14629ec7b004SRick Macklem 					if (*tl == newnfs_false)
14639ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
14649ec7b004SRick Macklem 				    } else {
14659ec7b004SRick Macklem 					if (*tl == newnfs_true)
14669ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
14679ec7b004SRick Macklem 				    }
14689ec7b004SRick Macklem 				}
14699ec7b004SRick Macklem 			} else if (fsp != NULL) {
14709ec7b004SRick Macklem 				if (*tl == newnfs_true)
14719ec7b004SRick Macklem 					fsp->fs_properties |= NFSV3_FSFLINK;
14729ec7b004SRick Macklem 				else
14739ec7b004SRick Macklem 					fsp->fs_properties &= ~NFSV3_FSFLINK;
14749ec7b004SRick Macklem 			}
14759ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
14769ec7b004SRick Macklem 			break;
14779ec7b004SRick Macklem 		case NFSATTRBIT_SYMLINKSUPPORT:
14789ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
14799ec7b004SRick Macklem 			if (compare) {
14809ec7b004SRick Macklem 				if (!(*retcmpp)) {
14819ec7b004SRick Macklem 				    if (fsp->fs_properties & NFSV3_FSFSYMLINK) {
14829ec7b004SRick Macklem 					if (*tl == newnfs_false)
14839ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
14849ec7b004SRick Macklem 				    } else {
14859ec7b004SRick Macklem 					if (*tl == newnfs_true)
14869ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
14879ec7b004SRick Macklem 				    }
14889ec7b004SRick Macklem 				}
14899ec7b004SRick Macklem 			} else if (fsp != NULL) {
14909ec7b004SRick Macklem 				if (*tl == newnfs_true)
14919ec7b004SRick Macklem 					fsp->fs_properties |= NFSV3_FSFSYMLINK;
14929ec7b004SRick Macklem 				else
14939ec7b004SRick Macklem 					fsp->fs_properties &= ~NFSV3_FSFSYMLINK;
14949ec7b004SRick Macklem 			}
14959ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
14969ec7b004SRick Macklem 			break;
14979ec7b004SRick Macklem 		case NFSATTRBIT_NAMEDATTR:
14989ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
14999ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
15009ec7b004SRick Macklem 				if (*tl != newnfs_false)
15019ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
15029ec7b004SRick Macklem 			}
15039ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
15049ec7b004SRick Macklem 			break;
15059ec7b004SRick Macklem 		case NFSATTRBIT_FSID:
15069ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
15079ec7b004SRick Macklem 			thyp = fxdr_hyper(tl);
15089ec7b004SRick Macklem 			tl += 2;
15099ec7b004SRick Macklem 			thyp2 = fxdr_hyper(tl);
15109ec7b004SRick Macklem 			if (compare) {
15119ec7b004SRick Macklem 			    if (*retcmpp == 0) {
15129ec7b004SRick Macklem 				if (thyp != (u_int64_t)
1513eea79fdeSAlan Somers 				    vp->v_mount->mnt_stat.f_fsid.val[0] ||
15149ec7b004SRick Macklem 				    thyp2 != (u_int64_t)
1515eea79fdeSAlan Somers 				    vp->v_mount->mnt_stat.f_fsid.val[1])
15169ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
15179ec7b004SRick Macklem 			    }
15189ec7b004SRick Macklem 			} else if (nap != NULL) {
15199ec7b004SRick Macklem 				nap->na_filesid[0] = thyp;
15209ec7b004SRick Macklem 				nap->na_filesid[1] = thyp2;
15219ec7b004SRick Macklem 			}
15229ec7b004SRick Macklem 			attrsum += (4 * NFSX_UNSIGNED);
15239ec7b004SRick Macklem 			break;
15249ec7b004SRick Macklem 		case NFSATTRBIT_UNIQUEHANDLES:
15259ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
15269ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
15279ec7b004SRick Macklem 				if (*tl != newnfs_true)
15289ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
15299ec7b004SRick Macklem 			}
15309ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
15319ec7b004SRick Macklem 			break;
15329ec7b004SRick Macklem 		case NFSATTRBIT_LEASETIME:
15339ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
15349ec7b004SRick Macklem 			if (compare) {
15359ec7b004SRick Macklem 				if (fxdr_unsigned(int, *tl) != nfsrv_lease &&
15369ec7b004SRick Macklem 				    !(*retcmpp))
15379ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
15389ec7b004SRick Macklem 			} else if (leasep != NULL) {
15399ec7b004SRick Macklem 				*leasep = fxdr_unsigned(u_int32_t, *tl);
15409ec7b004SRick Macklem 			}
15419ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
15429ec7b004SRick Macklem 			break;
15439ec7b004SRick Macklem 		case NFSATTRBIT_RDATTRERROR:
15449ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
15459ec7b004SRick Macklem 			if (compare) {
15469ec7b004SRick Macklem 				 if (!(*retcmpp))
15479ec7b004SRick Macklem 					*retcmpp = NFSERR_INVAL;
15489ec7b004SRick Macklem 			} else if (rderrp != NULL) {
15499ec7b004SRick Macklem 				*rderrp = fxdr_unsigned(u_int32_t, *tl);
15509ec7b004SRick Macklem 			}
15519ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
15529ec7b004SRick Macklem 			break;
15539ec7b004SRick Macklem 		case NFSATTRBIT_ACL:
15549ec7b004SRick Macklem 			if (compare) {
15559ec7b004SRick Macklem 			  if (!(*retcmpp)) {
1556d8a5961fSMarcelo Araujo 			    if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
15579ec7b004SRick Macklem 				NFSACL_T *naclp;
15589ec7b004SRick Macklem 
1559c3e22f83SRick Macklem 				naclp = acl_alloc(M_WAITOK);
1560a91a5784SRick Macklem 				error = nfsrv_dissectacl(nd, naclp, true,
1561a91a5784SRick Macklem 				    &aceerr, &cnt, p);
15629ec7b004SRick Macklem 				if (error) {
15639ec7b004SRick Macklem 				    acl_free(naclp);
1564a9285ae5SZack Kirsch 				    goto nfsmout;
15659ec7b004SRick Macklem 				}
1566061c683cSZack Kirsch 				if (aceerr || aclp == NULL ||
1567061c683cSZack Kirsch 				    nfsrv_compareacl(aclp, naclp))
15689ec7b004SRick Macklem 				    *retcmpp = NFSERR_NOTSAME;
15699ec7b004SRick Macklem 				acl_free(naclp);
157074991298SEdward Tomasz Napierala 			    } else {
1571a91a5784SRick Macklem 				error = nfsrv_dissectacl(nd, NULL, true,
1572a91a5784SRick Macklem 				    &aceerr, &cnt, p);
1573db0ac6deSCy Schubert 				if (error)
1574db0ac6deSCy Schubert 				    goto nfsmout;
15759ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
15769ec7b004SRick Macklem 			    }
15779ec7b004SRick Macklem 			  }
15789ec7b004SRick Macklem 			} else {
15799ec7b004SRick Macklem 				if (vp != NULL && aclp != NULL)
1580a91a5784SRick Macklem 				    error = nfsrv_dissectacl(nd, aclp, false,
1581a91a5784SRick Macklem 					&aceerr, &cnt, p);
15829ec7b004SRick Macklem 				else
1583a91a5784SRick Macklem 				    error = nfsrv_dissectacl(nd, NULL, false,
1584a91a5784SRick Macklem 					&aceerr, &cnt, p);
15859ec7b004SRick Macklem 				if (error)
1586a9285ae5SZack Kirsch 				    goto nfsmout;
15879ec7b004SRick Macklem 			}
1588d8a5961fSMarcelo Araujo 
15899ec7b004SRick Macklem 			attrsum += cnt;
15909ec7b004SRick Macklem 			break;
15919ec7b004SRick Macklem 		case NFSATTRBIT_ACLSUPPORT:
15929ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
15939ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
1594d8a5961fSMarcelo Araujo 				if (nfsrv_useacl && nfs_supportsnfsv4acls(vp)) {
15959ec7b004SRick Macklem 					if (fxdr_unsigned(u_int32_t, *tl) !=
15969ec7b004SRick Macklem 					    NFSV4ACE_SUPTYPES)
15979ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
15989ec7b004SRick Macklem 				} else {
15999ec7b004SRick Macklem 					*retcmpp = NFSERR_ATTRNOTSUPP;
16009ec7b004SRick Macklem 				}
16019ec7b004SRick Macklem 			}
16029ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
16039ec7b004SRick Macklem 			break;
16049ec7b004SRick Macklem 		case NFSATTRBIT_ARCHIVE:
16059ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
16069ec7b004SRick Macklem 			if (compare && !(*retcmpp))
16079ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
16089ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
16099ec7b004SRick Macklem 			break;
16109ec7b004SRick Macklem 		case NFSATTRBIT_CANSETTIME:
16119ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
16129ec7b004SRick Macklem 			if (compare) {
16139ec7b004SRick Macklem 				if (!(*retcmpp)) {
16149ec7b004SRick Macklem 				    if (fsp->fs_properties & NFSV3_FSFCANSETTIME) {
16159ec7b004SRick Macklem 					if (*tl == newnfs_false)
16169ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
16179ec7b004SRick Macklem 				    } else {
16189ec7b004SRick Macklem 					if (*tl == newnfs_true)
16199ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
16209ec7b004SRick Macklem 				    }
16219ec7b004SRick Macklem 				}
16229ec7b004SRick Macklem 			} else if (fsp != NULL) {
16239ec7b004SRick Macklem 				if (*tl == newnfs_true)
16249ec7b004SRick Macklem 					fsp->fs_properties |= NFSV3_FSFCANSETTIME;
16259ec7b004SRick Macklem 				else
16269ec7b004SRick Macklem 					fsp->fs_properties &= ~NFSV3_FSFCANSETTIME;
16279ec7b004SRick Macklem 			}
16289ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
16299ec7b004SRick Macklem 			break;
16309ec7b004SRick Macklem 		case NFSATTRBIT_CASEINSENSITIVE:
16319ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
16329ec7b004SRick Macklem 			if (compare) {
16339ec7b004SRick Macklem 				if (!(*retcmpp)) {
16349ec7b004SRick Macklem 				    if (*tl != newnfs_false)
16359ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
16369ec7b004SRick Macklem 				}
16379ec7b004SRick Macklem 			} else if (pc != NULL) {
16389ec7b004SRick Macklem 				pc->pc_caseinsensitive =
16399ec7b004SRick Macklem 				    fxdr_unsigned(u_int32_t, *tl);
16409ec7b004SRick Macklem 			}
16419ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
16429ec7b004SRick Macklem 			break;
16439ec7b004SRick Macklem 		case NFSATTRBIT_CASEPRESERVING:
16449ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
16459ec7b004SRick Macklem 			if (compare) {
16469ec7b004SRick Macklem 				if (!(*retcmpp)) {
16479ec7b004SRick Macklem 				    if (*tl != newnfs_true)
16489ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
16499ec7b004SRick Macklem 				}
16509ec7b004SRick Macklem 			} else if (pc != NULL) {
16519ec7b004SRick Macklem 				pc->pc_casepreserving =
16529ec7b004SRick Macklem 				    fxdr_unsigned(u_int32_t, *tl);
16539ec7b004SRick Macklem 			}
16549ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
16559ec7b004SRick Macklem 			break;
16569ec7b004SRick Macklem 		case NFSATTRBIT_CHOWNRESTRICTED:
16579ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
16589ec7b004SRick Macklem 			if (compare) {
16599ec7b004SRick Macklem 				if (!(*retcmpp)) {
1660061c683cSZack Kirsch 				    if (*tl != newnfs_true)
16619ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
16629ec7b004SRick Macklem 				}
16639ec7b004SRick Macklem 			} else if (pc != NULL) {
16649ec7b004SRick Macklem 				pc->pc_chownrestricted =
16659ec7b004SRick Macklem 				    fxdr_unsigned(u_int32_t, *tl);
16669ec7b004SRick Macklem 			}
16679ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
16689ec7b004SRick Macklem 			break;
16699ec7b004SRick Macklem 		case NFSATTRBIT_FILEHANDLE:
16709ec7b004SRick Macklem 			error = nfsm_getfh(nd, &tnfhp);
16719ec7b004SRick Macklem 			if (error)
1672a9285ae5SZack Kirsch 				goto nfsmout;
16739ec7b004SRick Macklem 			tfhsize = tnfhp->nfh_len;
16749ec7b004SRick Macklem 			if (compare) {
16759ec7b004SRick Macklem 				if (!(*retcmpp) &&
16769ec7b004SRick Macklem 				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
16779ec7b004SRick Macklem 				     fhp, fhsize))
16789ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
1679222daa42SConrad Meyer 				free(tnfhp, M_NFSFH);
16809ec7b004SRick Macklem 			} else if (nfhpp != NULL) {
16819ec7b004SRick Macklem 				*nfhpp = tnfhp;
16829ec7b004SRick Macklem 			} else {
1683222daa42SConrad Meyer 				free(tnfhp, M_NFSFH);
16849ec7b004SRick Macklem 			}
16859ec7b004SRick Macklem 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(tfhsize));
16869ec7b004SRick Macklem 			break;
16879ec7b004SRick Macklem 		case NFSATTRBIT_FILEID:
16889ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
16899ec7b004SRick Macklem 			thyp = fxdr_hyper(tl);
16909ec7b004SRick Macklem 			if (compare) {
16919ec7b004SRick Macklem 				if (!(*retcmpp)) {
169295ac7f1aSRick Macklem 					if (nap->na_fileid != thyp)
16939ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
16949ec7b004SRick Macklem 				}
169595ac7f1aSRick Macklem 			} else if (nap != NULL)
16969ec7b004SRick Macklem 				nap->na_fileid = thyp;
16979ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
16989ec7b004SRick Macklem 			break;
16999ec7b004SRick Macklem 		case NFSATTRBIT_FILESAVAIL:
17009ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
17019ec7b004SRick Macklem 			if (compare) {
17022d90ef47SRick Macklem 				uquad = nfsv4_filesavail(sbp, vp->v_mount);
17032d90ef47SRick Macklem 				if (!(*retcmpp) && uquad != fxdr_hyper(tl))
17049ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
17059ec7b004SRick Macklem 			} else if (sfp != NULL) {
17069ec7b004SRick Macklem 				sfp->sf_afiles = fxdr_hyper(tl);
17079ec7b004SRick Macklem 			}
17089ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
17099ec7b004SRick Macklem 			break;
17109ec7b004SRick Macklem 		case NFSATTRBIT_FILESFREE:
17119ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
17129ec7b004SRick Macklem 			if (compare) {
17132d90ef47SRick Macklem 				uquad = (uint64_t)sbp->f_ffree;
17142d90ef47SRick Macklem 				if (!(*retcmpp) && uquad != fxdr_hyper(tl))
17159ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
17169ec7b004SRick Macklem 			} else if (sfp != NULL) {
17179ec7b004SRick Macklem 				sfp->sf_ffiles = fxdr_hyper(tl);
17189ec7b004SRick Macklem 			}
17199ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
17209ec7b004SRick Macklem 			break;
17219ec7b004SRick Macklem 		case NFSATTRBIT_FILESTOTAL:
17229ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
17239ec7b004SRick Macklem 			if (compare) {
17242d90ef47SRick Macklem 				uquad = sbp->f_files;
17252d90ef47SRick Macklem 				if (!(*retcmpp) && uquad != fxdr_hyper(tl))
17269ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
17279ec7b004SRick Macklem 			} else if (sfp != NULL) {
17289ec7b004SRick Macklem 				sfp->sf_tfiles = fxdr_hyper(tl);
17299ec7b004SRick Macklem 			}
17309ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
17319ec7b004SRick Macklem 			break;
17329ec7b004SRick Macklem 		case NFSATTRBIT_FSLOCATIONS:
17339ec7b004SRick Macklem 			error = nfsrv_getrefstr(nd, &cp, &cp2, &l, &m);
17349ec7b004SRick Macklem 			if (error)
1735a9285ae5SZack Kirsch 				goto nfsmout;
17369ec7b004SRick Macklem 			attrsum += l;
17379ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
17389ec7b004SRick Macklem 				refp = nfsv4root_getreferral(vp, NULL, 0);
17399ec7b004SRick Macklem 				if (refp != NULL) {
17409ec7b004SRick Macklem 					if (cp == NULL || cp2 == NULL ||
17419ec7b004SRick Macklem 					    strcmp(cp, "/") ||
17429ec7b004SRick Macklem 					    strcmp(cp2, refp->nfr_srvlist))
17439ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
17449ec7b004SRick Macklem 				} else if (m == 0) {
17459ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
17469ec7b004SRick Macklem 				}
17479ec7b004SRick Macklem 			}
17489ec7b004SRick Macklem 			if (cp != NULL)
17499ec7b004SRick Macklem 				free(cp, M_NFSSTRING);
17509ec7b004SRick Macklem 			if (cp2 != NULL)
17519ec7b004SRick Macklem 				free(cp2, M_NFSSTRING);
17529ec7b004SRick Macklem 			break;
17539ec7b004SRick Macklem 		case NFSATTRBIT_HIDDEN:
17549ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
17559ec7b004SRick Macklem 			if (compare && !(*retcmpp))
17569ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
17579ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
17589ec7b004SRick Macklem 			break;
17599ec7b004SRick Macklem 		case NFSATTRBIT_HOMOGENEOUS:
17609ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
17619ec7b004SRick Macklem 			if (compare) {
17629ec7b004SRick Macklem 				if (!(*retcmpp)) {
17639ec7b004SRick Macklem 				    if (fsp->fs_properties &
17649ec7b004SRick Macklem 					NFSV3_FSFHOMOGENEOUS) {
17659ec7b004SRick Macklem 					if (*tl == newnfs_false)
17669ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
17679ec7b004SRick Macklem 				    } else {
17689ec7b004SRick Macklem 					if (*tl == newnfs_true)
17699ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
17709ec7b004SRick Macklem 				    }
17719ec7b004SRick Macklem 				}
17729ec7b004SRick Macklem 			} else if (fsp != NULL) {
17739ec7b004SRick Macklem 				if (*tl == newnfs_true)
17749ec7b004SRick Macklem 				    fsp->fs_properties |= NFSV3_FSFHOMOGENEOUS;
17759ec7b004SRick Macklem 				else
17769ec7b004SRick Macklem 				    fsp->fs_properties &= ~NFSV3_FSFHOMOGENEOUS;
17779ec7b004SRick Macklem 			}
17789ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
17799ec7b004SRick Macklem 			break;
17809ec7b004SRick Macklem 		case NFSATTRBIT_MAXFILESIZE:
17819ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
17829ec7b004SRick Macklem 			tnfsquad.qval = fxdr_hyper(tl);
17839ec7b004SRick Macklem 			if (compare) {
17849ec7b004SRick Macklem 				if (!(*retcmpp)) {
17859ec7b004SRick Macklem 					tquad = NFSRV_MAXFILESIZE;
17869ec7b004SRick Macklem 					if (tquad != tnfsquad.qval)
17879ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
17889ec7b004SRick Macklem 				}
17899ec7b004SRick Macklem 			} else if (fsp != NULL) {
17909ec7b004SRick Macklem 				fsp->fs_maxfilesize = tnfsquad.qval;
17919ec7b004SRick Macklem 			}
17929ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
17939ec7b004SRick Macklem 			break;
17949ec7b004SRick Macklem 		case NFSATTRBIT_MAXLINK:
17959ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
17969ec7b004SRick Macklem 			if (compare) {
17979ec7b004SRick Macklem 				if (!(*retcmpp)) {
1798a0a073b1SJohn Baldwin 				    if (fxdr_unsigned(int, *tl) != NFS_LINK_MAX)
17999ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
18009ec7b004SRick Macklem 				}
18019ec7b004SRick Macklem 			} else if (pc != NULL) {
18029ec7b004SRick Macklem 				pc->pc_linkmax = fxdr_unsigned(u_int32_t, *tl);
18039ec7b004SRick Macklem 			}
18049ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
18059ec7b004SRick Macklem 			break;
18069ec7b004SRick Macklem 		case NFSATTRBIT_MAXNAME:
18079ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
18089ec7b004SRick Macklem 			if (compare) {
18099ec7b004SRick Macklem 				if (!(*retcmpp)) {
18109ec7b004SRick Macklem 				    if (fsp->fs_maxname !=
18119ec7b004SRick Macklem 					fxdr_unsigned(u_int32_t, *tl))
18129ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
18139ec7b004SRick Macklem 				}
18149ec7b004SRick Macklem 			} else {
18159ec7b004SRick Macklem 				tuint = fxdr_unsigned(u_int32_t, *tl);
18169ec7b004SRick Macklem 				/*
18179ec7b004SRick Macklem 				 * Some Linux NFSv4 servers report this
18189ec7b004SRick Macklem 				 * as 0 or 4billion, so I'll set it to
18199ec7b004SRick Macklem 				 * NFS_MAXNAMLEN. If a server actually creates
18209ec7b004SRick Macklem 				 * a name longer than NFS_MAXNAMLEN, it will
18219ec7b004SRick Macklem 				 * get an error back.
18229ec7b004SRick Macklem 				 */
18239ec7b004SRick Macklem 				if (tuint == 0 || tuint > NFS_MAXNAMLEN)
18249ec7b004SRick Macklem 					tuint = NFS_MAXNAMLEN;
18259ec7b004SRick Macklem 				if (fsp != NULL)
18269ec7b004SRick Macklem 					fsp->fs_maxname = tuint;
18279ec7b004SRick Macklem 				if (pc != NULL)
18289ec7b004SRick Macklem 					pc->pc_namemax = tuint;
18299ec7b004SRick Macklem 			}
18309ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
18319ec7b004SRick Macklem 			break;
18329ec7b004SRick Macklem 		case NFSATTRBIT_MAXREAD:
18339ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
18349ec7b004SRick Macklem 			if (compare) {
18359ec7b004SRick Macklem 				if (!(*retcmpp)) {
18369ec7b004SRick Macklem 				    if (fsp->fs_rtmax != fxdr_unsigned(u_int32_t,
18379ec7b004SRick Macklem 					*(tl + 1)) || *tl != 0)
18389ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
18399ec7b004SRick Macklem 				}
18409ec7b004SRick Macklem 			} else if (fsp != NULL) {
18419ec7b004SRick Macklem 				fsp->fs_rtmax = fxdr_unsigned(u_int32_t, *++tl);
18429ec7b004SRick Macklem 				fsp->fs_rtpref = fsp->fs_rtmax;
18439ec7b004SRick Macklem 				fsp->fs_dtpref = fsp->fs_rtpref;
18449ec7b004SRick Macklem 			}
18459ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
18469ec7b004SRick Macklem 			break;
18479ec7b004SRick Macklem 		case NFSATTRBIT_MAXWRITE:
18489ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
18499ec7b004SRick Macklem 			if (compare) {
18509ec7b004SRick Macklem 				if (!(*retcmpp)) {
18519ec7b004SRick Macklem 				    if (fsp->fs_wtmax != fxdr_unsigned(u_int32_t,
18529ec7b004SRick Macklem 					*(tl + 1)) || *tl != 0)
18539ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
18549ec7b004SRick Macklem 				}
18559ec7b004SRick Macklem 			} else if (fsp != NULL) {
18569ec7b004SRick Macklem 				fsp->fs_wtmax = fxdr_unsigned(int, *++tl);
18579ec7b004SRick Macklem 				fsp->fs_wtpref = fsp->fs_wtmax;
18589ec7b004SRick Macklem 			}
18599ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
18609ec7b004SRick Macklem 			break;
18619ec7b004SRick Macklem 		case NFSATTRBIT_MIMETYPE:
18629ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
18639ec7b004SRick Macklem 			i = fxdr_unsigned(int, *tl);
18649ec7b004SRick Macklem 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(i));
18659ec7b004SRick Macklem 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
18669ec7b004SRick Macklem 			if (error)
18679ec7b004SRick Macklem 				goto nfsmout;
18689ec7b004SRick Macklem 			if (compare && !(*retcmpp))
18699ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
18709ec7b004SRick Macklem 			break;
18719ec7b004SRick Macklem 		case NFSATTRBIT_MODE:
18729ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
18739ec7b004SRick Macklem 			if (compare) {
18749ec7b004SRick Macklem 				if (!(*retcmpp)) {
18759ec7b004SRick Macklem 				    if (nap->na_mode != nfstov_mode(*tl))
18769ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
18779ec7b004SRick Macklem 				}
18789ec7b004SRick Macklem 			} else if (nap != NULL) {
18799ec7b004SRick Macklem 				nap->na_mode = nfstov_mode(*tl);
18809ec7b004SRick Macklem 			}
18819ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
18829ec7b004SRick Macklem 			break;
18839ec7b004SRick Macklem 		case NFSATTRBIT_NOTRUNC:
18849ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
18859ec7b004SRick Macklem 			if (compare) {
18869ec7b004SRick Macklem 				if (!(*retcmpp)) {
18879ec7b004SRick Macklem 				    if (*tl != newnfs_true)
18889ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
18899ec7b004SRick Macklem 				}
18909ec7b004SRick Macklem 			} else if (pc != NULL) {
18919ec7b004SRick Macklem 				pc->pc_notrunc = fxdr_unsigned(u_int32_t, *tl);
18929ec7b004SRick Macklem 			}
18939ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
18949ec7b004SRick Macklem 			break;
18959ec7b004SRick Macklem 		case NFSATTRBIT_NUMLINKS:
18969ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
18979ec7b004SRick Macklem 			tuint = fxdr_unsigned(u_int32_t, *tl);
18989ec7b004SRick Macklem 			if (compare) {
18999ec7b004SRick Macklem 			    if (!(*retcmpp)) {
19009ec7b004SRick Macklem 				if ((u_int32_t)nap->na_nlink != tuint)
19019ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
19029ec7b004SRick Macklem 			    }
19039ec7b004SRick Macklem 			} else if (nap != NULL) {
19049ec7b004SRick Macklem 				nap->na_nlink = tuint;
19059ec7b004SRick Macklem 			}
19069ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
19079ec7b004SRick Macklem 			break;
19089ec7b004SRick Macklem 		case NFSATTRBIT_OWNER:
19099ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
19109ec7b004SRick Macklem 			j = fxdr_unsigned(int, *tl);
1911ef4edb70SRick Macklem 			if (j < 0 || j > NFSV4_MAXOWNERGROUPLEN) {
1912a9285ae5SZack Kirsch 				error = NFSERR_BADXDR;
1913a9285ae5SZack Kirsch 				goto nfsmout;
1914a9285ae5SZack Kirsch 			}
19159ec7b004SRick Macklem 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
19169ec7b004SRick Macklem 			if (j > NFSV4_SMALLSTR)
19179ec7b004SRick Macklem 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
19189ec7b004SRick Macklem 			else
19199ec7b004SRick Macklem 				cp = namestr;
19209ec7b004SRick Macklem 			error = nfsrv_mtostr(nd, cp, j);
19219ec7b004SRick Macklem 			if (error) {
19229ec7b004SRick Macklem 				if (j > NFSV4_SMALLSTR)
19239ec7b004SRick Macklem 					free(cp, M_NFSSTRING);
1924a9285ae5SZack Kirsch 				goto nfsmout;
19259ec7b004SRick Macklem 			}
19269ec7b004SRick Macklem 			if (compare) {
19279ec7b004SRick Macklem 			    if (!(*retcmpp)) {
19280658ac39SEdward Tomasz Napierala 				if (nfsv4_strtouid(nd, cp, j, &uid) ||
19299ec7b004SRick Macklem 				    nap->na_uid != uid)
19309ec7b004SRick Macklem 				    *retcmpp = NFSERR_NOTSAME;
19319ec7b004SRick Macklem 			    }
19329ec7b004SRick Macklem 			} else if (nap != NULL) {
19330658ac39SEdward Tomasz Napierala 				if (nfsv4_strtouid(nd, cp, j, &uid))
1934f0db2b60SRick Macklem 					nap->na_uid =
1935f0db2b60SRick Macklem 					    NFSD_VNET(nfsrv_defaultuid);
19369ec7b004SRick Macklem 				else
19379ec7b004SRick Macklem 					nap->na_uid = uid;
19389ec7b004SRick Macklem 			}
19399ec7b004SRick Macklem 			if (j > NFSV4_SMALLSTR)
19409ec7b004SRick Macklem 				free(cp, M_NFSSTRING);
19419ec7b004SRick Macklem 			break;
19429ec7b004SRick Macklem 		case NFSATTRBIT_OWNERGROUP:
19439ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
19449ec7b004SRick Macklem 			j = fxdr_unsigned(int, *tl);
1945ef4edb70SRick Macklem 			if (j < 0 || j > NFSV4_MAXOWNERGROUPLEN) {
1946a9285ae5SZack Kirsch 				error =  NFSERR_BADXDR;
1947a9285ae5SZack Kirsch 				goto nfsmout;
1948a9285ae5SZack Kirsch 			}
19499ec7b004SRick Macklem 			attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
19509ec7b004SRick Macklem 			if (j > NFSV4_SMALLSTR)
19519ec7b004SRick Macklem 				cp = malloc(j + 1, M_NFSSTRING, M_WAITOK);
19529ec7b004SRick Macklem 			else
19539ec7b004SRick Macklem 				cp = namestr;
19549ec7b004SRick Macklem 			error = nfsrv_mtostr(nd, cp, j);
19559ec7b004SRick Macklem 			if (error) {
19569ec7b004SRick Macklem 				if (j > NFSV4_SMALLSTR)
19579ec7b004SRick Macklem 					free(cp, M_NFSSTRING);
1958a9285ae5SZack Kirsch 				goto nfsmout;
19599ec7b004SRick Macklem 			}
19609ec7b004SRick Macklem 			if (compare) {
19619ec7b004SRick Macklem 			    if (!(*retcmpp)) {
19622df8bd90SEdward Tomasz Napierala 				if (nfsv4_strtogid(nd, cp, j, &gid) ||
19639ec7b004SRick Macklem 				    nap->na_gid != gid)
19649ec7b004SRick Macklem 				    *retcmpp = NFSERR_NOTSAME;
19659ec7b004SRick Macklem 			    }
19669ec7b004SRick Macklem 			} else if (nap != NULL) {
19672df8bd90SEdward Tomasz Napierala 				if (nfsv4_strtogid(nd, cp, j, &gid))
1968f0db2b60SRick Macklem 					nap->na_gid =
1969f0db2b60SRick Macklem 					    NFSD_VNET(nfsrv_defaultgid);
19709ec7b004SRick Macklem 				else
19719ec7b004SRick Macklem 					nap->na_gid = gid;
19729ec7b004SRick Macklem 			}
19739ec7b004SRick Macklem 			if (j > NFSV4_SMALLSTR)
19749ec7b004SRick Macklem 				free(cp, M_NFSSTRING);
19759ec7b004SRick Macklem 			break;
19769ec7b004SRick Macklem 		case NFSATTRBIT_QUOTAHARD:
19779ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
19789ec7b004SRick Macklem 			if (sbp != NULL) {
1979cc426dd3SMateusz Guzik 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA))
19809ec7b004SRick Macklem 				freenum = sbp->f_bfree;
19819ec7b004SRick Macklem 			    else
19829ec7b004SRick Macklem 				freenum = sbp->f_bavail;
19839ec7b004SRick Macklem #ifdef QUOTA
19849ec7b004SRick Macklem 			    /*
19859ec7b004SRick Macklem 			     * ufs_quotactl() insists that the uid argument
19869ec7b004SRick Macklem 			     * equal p_ruid for non-root quota access, so
19879ec7b004SRick Macklem 			     * we'll just make sure that's the case.
19889ec7b004SRick Macklem 			     */
19899ec7b004SRick Macklem 			    savuid = p->p_cred->p_ruid;
19909ec7b004SRick Macklem 			    p->p_cred->p_ruid = cred->cr_uid;
1991eea79fdeSAlan Somers 			    if (!VFS_QUOTACTL(vp->v_mount,QCMD(Q_GETQUOTA,
199252e63ec2SBrooks Davis 				USRQUOTA), cred->cr_uid, &dqb))
19939ec7b004SRick Macklem 				freenum = min(dqb.dqb_bhardlimit, freenum);
19949ec7b004SRick Macklem 			    p->p_cred->p_ruid = savuid;
19959ec7b004SRick Macklem #endif	/* QUOTA */
19969ec7b004SRick Macklem 			    uquad = (u_int64_t)freenum;
19979ec7b004SRick Macklem 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
19989ec7b004SRick Macklem 			}
19999ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
20009ec7b004SRick Macklem 				if (uquad != fxdr_hyper(tl))
20019ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
20029ec7b004SRick Macklem 			}
20039ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
20049ec7b004SRick Macklem 			break;
20059ec7b004SRick Macklem 		case NFSATTRBIT_QUOTASOFT:
20069ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
20079ec7b004SRick Macklem 			if (sbp != NULL) {
2008cc426dd3SMateusz Guzik 			    if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA))
20099ec7b004SRick Macklem 				freenum = sbp->f_bfree;
20109ec7b004SRick Macklem 			    else
20119ec7b004SRick Macklem 				freenum = sbp->f_bavail;
20129ec7b004SRick Macklem #ifdef QUOTA
20139ec7b004SRick Macklem 			    /*
20149ec7b004SRick Macklem 			     * ufs_quotactl() insists that the uid argument
20159ec7b004SRick Macklem 			     * equal p_ruid for non-root quota access, so
20169ec7b004SRick Macklem 			     * we'll just make sure that's the case.
20179ec7b004SRick Macklem 			     */
20189ec7b004SRick Macklem 			    savuid = p->p_cred->p_ruid;
20199ec7b004SRick Macklem 			    p->p_cred->p_ruid = cred->cr_uid;
2020eea79fdeSAlan Somers 			    if (!VFS_QUOTACTL(vp->v_mount,QCMD(Q_GETQUOTA,
202152e63ec2SBrooks Davis 				USRQUOTA), cred->cr_uid, &dqb))
20229ec7b004SRick Macklem 				freenum = min(dqb.dqb_bsoftlimit, freenum);
20239ec7b004SRick Macklem 			    p->p_cred->p_ruid = savuid;
20249ec7b004SRick Macklem #endif	/* QUOTA */
20259ec7b004SRick Macklem 			    uquad = (u_int64_t)freenum;
20269ec7b004SRick Macklem 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
20279ec7b004SRick Macklem 			}
20289ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
20299ec7b004SRick Macklem 				if (uquad != fxdr_hyper(tl))
20309ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
20319ec7b004SRick Macklem 			}
20329ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
20339ec7b004SRick Macklem 			break;
20349ec7b004SRick Macklem 		case NFSATTRBIT_QUOTAUSED:
20359ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
20369ec7b004SRick Macklem 			if (sbp != NULL) {
20379ec7b004SRick Macklem 			    freenum = 0;
20389ec7b004SRick Macklem #ifdef QUOTA
20399ec7b004SRick Macklem 			    /*
20409ec7b004SRick Macklem 			     * ufs_quotactl() insists that the uid argument
20419ec7b004SRick Macklem 			     * equal p_ruid for non-root quota access, so
20429ec7b004SRick Macklem 			     * we'll just make sure that's the case.
20439ec7b004SRick Macklem 			     */
20449ec7b004SRick Macklem 			    savuid = p->p_cred->p_ruid;
20459ec7b004SRick Macklem 			    p->p_cred->p_ruid = cred->cr_uid;
2046eea79fdeSAlan Somers 			    if (!VFS_QUOTACTL(vp->v_mount,QCMD(Q_GETQUOTA,
204752e63ec2SBrooks Davis 				USRQUOTA), cred->cr_uid, &dqb))
20489ec7b004SRick Macklem 				freenum = dqb.dqb_curblocks;
20499ec7b004SRick Macklem 			    p->p_cred->p_ruid = savuid;
20509ec7b004SRick Macklem #endif	/* QUOTA */
20519ec7b004SRick Macklem 			    uquad = (u_int64_t)freenum;
20529ec7b004SRick Macklem 			    NFSQUOTABLKTOBYTE(uquad, sbp->f_bsize);
20539ec7b004SRick Macklem 			}
20549ec7b004SRick Macklem 			if (compare && !(*retcmpp)) {
20559ec7b004SRick Macklem 				if (uquad != fxdr_hyper(tl))
20569ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
20579ec7b004SRick Macklem 			}
20589ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
20599ec7b004SRick Macklem 			break;
20609ec7b004SRick Macklem 		case NFSATTRBIT_RAWDEV:
20619ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4SPECDATA);
20629ec7b004SRick Macklem 			j = fxdr_unsigned(int, *tl++);
20639ec7b004SRick Macklem 			k = fxdr_unsigned(int, *tl);
20649ec7b004SRick Macklem 			if (compare) {
20659ec7b004SRick Macklem 			    if (!(*retcmpp)) {
20669ec7b004SRick Macklem 				if (nap->na_rdev != NFSMAKEDEV(j, k))
20679ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
20689ec7b004SRick Macklem 			    }
20699ec7b004SRick Macklem 			} else if (nap != NULL) {
20709ec7b004SRick Macklem 				nap->na_rdev = NFSMAKEDEV(j, k);
20719ec7b004SRick Macklem 			}
20729ec7b004SRick Macklem 			attrsum += NFSX_V4SPECDATA;
20739ec7b004SRick Macklem 			break;
20749ec7b004SRick Macklem 		case NFSATTRBIT_SPACEAVAIL:
20759ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
20769ec7b004SRick Macklem 			if (compare) {
20772d90ef47SRick Macklem 				if (priv_check_cred(cred,
20782d90ef47SRick Macklem 				    PRIV_VFS_BLOCKRESERVE))
20792d90ef47SRick Macklem 					uquad = sbp->f_bfree;
20802d90ef47SRick Macklem 				else
20812d90ef47SRick Macklem 					uquad = (uint64_t)sbp->f_bavail;
20822d90ef47SRick Macklem 				uquad *= sbp->f_bsize;
20832d90ef47SRick Macklem 				if (!(*retcmpp) && uquad != fxdr_hyper(tl))
20849ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
20859ec7b004SRick Macklem 			} else if (sfp != NULL) {
20869ec7b004SRick Macklem 				sfp->sf_abytes = fxdr_hyper(tl);
20879ec7b004SRick Macklem 			}
20889ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
20899ec7b004SRick Macklem 			break;
20909ec7b004SRick Macklem 		case NFSATTRBIT_SPACEFREE:
20919ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
20929ec7b004SRick Macklem 			if (compare) {
20932d90ef47SRick Macklem 				uquad = sbp->f_bfree;
20942d90ef47SRick Macklem 				uquad *= sbp->f_bsize;
20952d90ef47SRick Macklem 				if (!(*retcmpp) && uquad != fxdr_hyper(tl))
20969ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
20979ec7b004SRick Macklem 			} else if (sfp != NULL) {
20989ec7b004SRick Macklem 				sfp->sf_fbytes = fxdr_hyper(tl);
20999ec7b004SRick Macklem 			}
21009ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
21019ec7b004SRick Macklem 			break;
21029ec7b004SRick Macklem 		case NFSATTRBIT_SPACETOTAL:
21039ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
21049ec7b004SRick Macklem 			if (compare) {
21052d90ef47SRick Macklem 				uquad = sbp->f_blocks;
21062d90ef47SRick Macklem 				uquad *= sbp->f_bsize;
21072d90ef47SRick Macklem 				if (!(*retcmpp) && uquad != fxdr_hyper(tl))
21089ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
21099ec7b004SRick Macklem 			} else if (sfp != NULL) {
21109ec7b004SRick Macklem 				sfp->sf_tbytes = fxdr_hyper(tl);
21119ec7b004SRick Macklem 			}
21129ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
21139ec7b004SRick Macklem 			break;
21149ec7b004SRick Macklem 		case NFSATTRBIT_SPACEUSED:
21159ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
21169ec7b004SRick Macklem 			thyp = fxdr_hyper(tl);
21179ec7b004SRick Macklem 			if (compare) {
21189ec7b004SRick Macklem 			    if (!(*retcmpp)) {
21199ec7b004SRick Macklem 				if ((u_int64_t)nap->na_bytes != thyp)
21209ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
21219ec7b004SRick Macklem 			    }
21229ec7b004SRick Macklem 			} else if (nap != NULL) {
21239ec7b004SRick Macklem 				nap->na_bytes = thyp;
21249ec7b004SRick Macklem 			}
21259ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
21269ec7b004SRick Macklem 			break;
21279ec7b004SRick Macklem 		case NFSATTRBIT_SYSTEM:
21289ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
21299ec7b004SRick Macklem 			if (compare && !(*retcmpp))
21309ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
21319ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
21329ec7b004SRick Macklem 			break;
21339ec7b004SRick Macklem 		case NFSATTRBIT_TIMEACCESS:
21349ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
21359ec7b004SRick Macklem 			fxdr_nfsv4time(tl, &temptime);
21369ec7b004SRick Macklem 			if (compare) {
21379ec7b004SRick Macklem 			    if (!(*retcmpp)) {
21389ec7b004SRick Macklem 				if (!NFS_CMPTIME(temptime, nap->na_atime))
21399ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
21409ec7b004SRick Macklem 			    }
21419ec7b004SRick Macklem 			} else if (nap != NULL) {
21429ec7b004SRick Macklem 				nap->na_atime = temptime;
21439ec7b004SRick Macklem 			}
21449ec7b004SRick Macklem 			attrsum += NFSX_V4TIME;
21459ec7b004SRick Macklem 			break;
21469ec7b004SRick Macklem 		case NFSATTRBIT_TIMEACCESSSET:
21479ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
21489ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
21499ec7b004SRick Macklem 			i = fxdr_unsigned(int, *tl);
21509ec7b004SRick Macklem 			if (i == NFSV4SATTRTIME_TOCLIENT) {
21519ec7b004SRick Macklem 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
21529ec7b004SRick Macklem 				attrsum += NFSX_V4TIME;
21539ec7b004SRick Macklem 			}
21549ec7b004SRick Macklem 			if (compare && !(*retcmpp))
21559ec7b004SRick Macklem 				*retcmpp = NFSERR_INVAL;
21569ec7b004SRick Macklem 			break;
21579ec7b004SRick Macklem 		case NFSATTRBIT_TIMEBACKUP:
21589ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
21599ec7b004SRick Macklem 			if (compare && !(*retcmpp))
21609ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
21619ec7b004SRick Macklem 			attrsum += NFSX_V4TIME;
21629ec7b004SRick Macklem 			break;
21639ec7b004SRick Macklem 		case NFSATTRBIT_TIMECREATE:
21649ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
2165c07782e1SDoug Rabson 			fxdr_nfsv4time(tl, &temptime);
2166c07782e1SDoug Rabson 			if (compare) {
2167c07782e1SDoug Rabson 			    if (!(*retcmpp)) {
2168c07782e1SDoug Rabson 				if (!NFS_CMPTIME(temptime, nap->na_btime))
2169c07782e1SDoug Rabson 					*retcmpp = NFSERR_NOTSAME;
2170c07782e1SDoug Rabson 			    }
2171c07782e1SDoug Rabson 			} else if (nap != NULL) {
2172c07782e1SDoug Rabson 				nap->na_btime = temptime;
2173c07782e1SDoug Rabson 			}
21749ec7b004SRick Macklem 			attrsum += NFSX_V4TIME;
21759ec7b004SRick Macklem 			break;
21769ec7b004SRick Macklem 		case NFSATTRBIT_TIMEDELTA:
21779ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
21789ec7b004SRick Macklem 			if (fsp != NULL) {
21799ec7b004SRick Macklem 			    if (compare) {
21809ec7b004SRick Macklem 				if (!(*retcmpp)) {
21819ec7b004SRick Macklem 				    if ((u_int32_t)fsp->fs_timedelta.tv_sec !=
21829ec7b004SRick Macklem 					fxdr_unsigned(u_int32_t, *(tl + 1)) ||
21839ec7b004SRick Macklem 				        (u_int32_t)fsp->fs_timedelta.tv_nsec !=
21849ec7b004SRick Macklem 					(fxdr_unsigned(u_int32_t, *(tl + 2)) %
21859ec7b004SRick Macklem 					 1000000000) ||
21869ec7b004SRick Macklem 					*tl != 0)
21879ec7b004SRick Macklem 					    *retcmpp = NFSERR_NOTSAME;
21889ec7b004SRick Macklem 				}
21899ec7b004SRick Macklem 			    } else {
21909ec7b004SRick Macklem 				fxdr_nfsv4time(tl, &fsp->fs_timedelta);
21919ec7b004SRick Macklem 			    }
21929ec7b004SRick Macklem 			}
21939ec7b004SRick Macklem 			attrsum += NFSX_V4TIME;
21949ec7b004SRick Macklem 			break;
21959ec7b004SRick Macklem 		case NFSATTRBIT_TIMEMETADATA:
21969ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
21979ec7b004SRick Macklem 			fxdr_nfsv4time(tl, &temptime);
21989ec7b004SRick Macklem 			if (compare) {
21999ec7b004SRick Macklem 			    if (!(*retcmpp)) {
22009ec7b004SRick Macklem 				if (!NFS_CMPTIME(temptime, nap->na_ctime))
22019ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
22029ec7b004SRick Macklem 			    }
22039ec7b004SRick Macklem 			} else if (nap != NULL) {
22049ec7b004SRick Macklem 				nap->na_ctime = temptime;
22059ec7b004SRick Macklem 			}
22069ec7b004SRick Macklem 			attrsum += NFSX_V4TIME;
22079ec7b004SRick Macklem 			break;
22089ec7b004SRick Macklem 		case NFSATTRBIT_TIMEMODIFY:
22099ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
22109ec7b004SRick Macklem 			fxdr_nfsv4time(tl, &temptime);
22119ec7b004SRick Macklem 			if (compare) {
22129ec7b004SRick Macklem 			    if (!(*retcmpp)) {
22139ec7b004SRick Macklem 				if (!NFS_CMPTIME(temptime, nap->na_mtime))
22149ec7b004SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
22159ec7b004SRick Macklem 			    }
22169ec7b004SRick Macklem 			} else if (nap != NULL) {
22179ec7b004SRick Macklem 				nap->na_mtime = temptime;
22189ec7b004SRick Macklem 			}
22199ec7b004SRick Macklem 			attrsum += NFSX_V4TIME;
22209ec7b004SRick Macklem 			break;
22219ec7b004SRick Macklem 		case NFSATTRBIT_TIMEMODIFYSET:
22229ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
22239ec7b004SRick Macklem 			attrsum += NFSX_UNSIGNED;
22249ec7b004SRick Macklem 			i = fxdr_unsigned(int, *tl);
22259ec7b004SRick Macklem 			if (i == NFSV4SATTRTIME_TOCLIENT) {
22269ec7b004SRick Macklem 				NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
22279ec7b004SRick Macklem 				attrsum += NFSX_V4TIME;
22289ec7b004SRick Macklem 			}
22299ec7b004SRick Macklem 			if (compare && !(*retcmpp))
22309ec7b004SRick Macklem 				*retcmpp = NFSERR_INVAL;
22319ec7b004SRick Macklem 			break;
22329ec7b004SRick Macklem 		case NFSATTRBIT_MOUNTEDONFILEID:
22339ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
22349ec7b004SRick Macklem 			thyp = fxdr_hyper(tl);
22359ec7b004SRick Macklem 			if (compare) {
22369ec7b004SRick Macklem 				if (!(*retcmpp)) {
223795ac7f1aSRick Macklem 					if (!vp || !nfsrv_atroot(vp, &thyp2))
223895ac7f1aSRick Macklem 						thyp2 = nap->na_fileid;
223995ac7f1aSRick Macklem 					if (thyp2 != thyp)
22409ec7b004SRick Macklem 						*retcmpp = NFSERR_NOTSAME;
22419ec7b004SRick Macklem 				}
224295ac7f1aSRick Macklem 			} else if (nap != NULL)
22439ec7b004SRick Macklem 				nap->na_mntonfileno = thyp;
22449ec7b004SRick Macklem 			attrsum += NFSX_HYPER;
22459ec7b004SRick Macklem 			break;
2246c59e4cc3SRick Macklem 		case NFSATTRBIT_SUPPATTREXCLCREAT:
2247c59e4cc3SRick Macklem 			retnotsup = 0;
2248c59e4cc3SRick Macklem 			error = nfsrv_getattrbits(nd, &retattrbits,
2249c59e4cc3SRick Macklem 			    &cnt, &retnotsup);
2250c59e4cc3SRick Macklem 			if (error)
2251c59e4cc3SRick Macklem 			    goto nfsmout;
2252c59e4cc3SRick Macklem 			if (compare && !(*retcmpp)) {
2253ea5776ecSRick Macklem 			   NFSSETSUPP_ATTRBIT(&checkattrbits, nd);
2254ea5776ecSRick Macklem 			   NFSCLRNOTSETABLE_ATTRBIT(&checkattrbits, nd);
2255c59e4cc3SRick Macklem 			   NFSCLRBIT_ATTRBIT(&checkattrbits,
2256c59e4cc3SRick Macklem 				NFSATTRBIT_TIMEACCESSSET);
2257c59e4cc3SRick Macklem 			   if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
2258c59e4cc3SRick Macklem 			       || retnotsup)
2259c59e4cc3SRick Macklem 				*retcmpp = NFSERR_NOTSAME;
2260c59e4cc3SRick Macklem 			}
2261c59e4cc3SRick Macklem 			attrsum += cnt;
2262c59e4cc3SRick Macklem 			break;
226390d2dfabSRick Macklem 		case NFSATTRBIT_FSLAYOUTTYPE:
226490d2dfabSRick Macklem 		case NFSATTRBIT_LAYOUTTYPE:
226590d2dfabSRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
226690d2dfabSRick Macklem 			attrsum += NFSX_UNSIGNED;
226790d2dfabSRick Macklem 			i = fxdr_unsigned(int, *tl);
2268480be96eSRick Macklem 			/*
2269480be96eSRick Macklem 			 * The RFCs do not define an upper limit for the
2270480be96eSRick Macklem 			 * number of layout types, but 32 should be more
2271480be96eSRick Macklem 			 * than enough.
2272480be96eSRick Macklem 			 */
2273480be96eSRick Macklem 			if (i < 0 || i > 32) {
2274480be96eSRick Macklem 				error = NFSERR_BADXDR;
2275480be96eSRick Macklem 				goto nfsmout;
2276480be96eSRick Macklem 			}
227790d2dfabSRick Macklem 			if (i > 0) {
227890d2dfabSRick Macklem 				NFSM_DISSECT(tl, u_int32_t *, i *
227990d2dfabSRick Macklem 				    NFSX_UNSIGNED);
228090d2dfabSRick Macklem 				attrsum += i * NFSX_UNSIGNED;
228190d2dfabSRick Macklem 				j = fxdr_unsigned(int, *tl);
228290d2dfabSRick Macklem 				if (i == 1 && compare && !(*retcmpp) &&
228390d2dfabSRick Macklem 				    (((nfsrv_doflexfile != 0 ||
228490d2dfabSRick Macklem 				       nfsrv_maxpnfsmirror > 1) &&
228590d2dfabSRick Macklem 				      j != NFSLAYOUT_FLEXFILE) ||
228690d2dfabSRick Macklem 				    (nfsrv_doflexfile == 0 &&
228790d2dfabSRick Macklem 				     j != NFSLAYOUT_NFSV4_1_FILES)))
228890d2dfabSRick Macklem 					*retcmpp = NFSERR_NOTSAME;
228990d2dfabSRick Macklem 			}
229090d2dfabSRick Macklem 			if (nfsrv_devidcnt == 0) {
229190d2dfabSRick Macklem 				if (compare && !(*retcmpp) && i > 0)
229290d2dfabSRick Macklem 					*retcmpp = NFSERR_NOTSAME;
229390d2dfabSRick Macklem 			} else {
229490d2dfabSRick Macklem 				if (compare && !(*retcmpp) && i != 1)
229590d2dfabSRick Macklem 					*retcmpp = NFSERR_NOTSAME;
229690d2dfabSRick Macklem 			}
229790d2dfabSRick Macklem 			break;
229890d2dfabSRick Macklem 		case NFSATTRBIT_LAYOUTALIGNMENT:
229990d2dfabSRick Macklem 		case NFSATTRBIT_LAYOUTBLKSIZE:
230090d2dfabSRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
230190d2dfabSRick Macklem 			attrsum += NFSX_UNSIGNED;
230290d2dfabSRick Macklem 			i = fxdr_unsigned(int, *tl);
2303ee29e6f3SRick Macklem 			if (compare && !(*retcmpp) && i != nfs_srvmaxio)
230490d2dfabSRick Macklem 				*retcmpp = NFSERR_NOTSAME;
230590d2dfabSRick Macklem 			break;
2306709c1891SRick Macklem 		case NFSATTRBIT_CHANGEATTRTYPE:
2307709c1891SRick Macklem 			NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
2308709c1891SRick Macklem 			if (compare) {
2309709c1891SRick Macklem 				if (!(*retcmpp)) {
2310709c1891SRick Macklem 				    tuint = NFSV4CHANGETYPE_UNDEFINED;
2311709c1891SRick Macklem 				    if ((vp->v_mount->mnt_vfc->vfc_flags &
2312709c1891SRick Macklem 					VFCF_FILEREVINC) != 0)
2313709c1891SRick Macklem 					tuint = NFSV4CHANGETYPE_VERS_COUNTER_NOPNFS;
2314709c1891SRick Macklem 				    else if ((vp->v_mount->mnt_vfc->vfc_flags &
2315709c1891SRick Macklem 					VFCF_FILEREVCT) != 0)
2316709c1891SRick Macklem 					tuint = NFSV4CHANGETYPE_TIME_METADATA;
2317709c1891SRick Macklem 				    if (fxdr_unsigned(uint32_t, *tl) != tuint)
2318709c1891SRick Macklem 					*retcmpp = NFSERR_NOTSAME;
2319709c1891SRick Macklem 				}
2320709c1891SRick Macklem 			}
2321709c1891SRick Macklem 			attrsum += NFSX_UNSIGNED;
2322709c1891SRick Macklem 			break;
23239ec7b004SRick Macklem 		default:
23249ec7b004SRick Macklem 			printf("EEK! nfsv4_loadattr unknown attr=%d\n",
23259ec7b004SRick Macklem 				bitpos);
23269ec7b004SRick Macklem 			if (compare && !(*retcmpp))
23279ec7b004SRick Macklem 				*retcmpp = NFSERR_ATTRNOTSUPP;
23289ec7b004SRick Macklem 			/*
23299ec7b004SRick Macklem 			 * and get out of the loop, since we can't parse
2330bf312482SGordon Bergling 			 * the unknown attribute data.
23319ec7b004SRick Macklem 			 */
23329ec7b004SRick Macklem 			bitpos = NFSATTRBIT_MAX;
23339ec7b004SRick Macklem 			break;
233474b8d63dSPedro F. Giffuni 		}
23359ec7b004SRick Macklem 	}
23369ec7b004SRick Macklem 
23379ec7b004SRick Macklem 	/*
23389ec7b004SRick Macklem 	 * some clients pad the attrlist, so we need to skip over the
23399ec7b004SRick Macklem 	 * padding.
23409ec7b004SRick Macklem 	 */
23419ec7b004SRick Macklem 	if (attrsum > attrsize) {
23429ec7b004SRick Macklem 		error = NFSERR_BADXDR;
23439ec7b004SRick Macklem 	} else {
23449ec7b004SRick Macklem 		attrsize = NFSM_RNDUP(attrsize);
23459ec7b004SRick Macklem 		if (attrsum < attrsize)
23469ec7b004SRick Macklem 			error = nfsm_advance(nd, attrsize - attrsum, -1);
23479ec7b004SRick Macklem 	}
23489ec7b004SRick Macklem nfsmout:
2349f0db2b60SRick Macklem 	NFSD_CURVNET_RESTORE();
2350a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
23519ec7b004SRick Macklem 	return (error);
23529ec7b004SRick Macklem }
23539ec7b004SRick Macklem 
23549ec7b004SRick Macklem /*
23559ec7b004SRick Macklem  * Implement sleep locks for newnfs. The nfslock_usecnt allows for a
23569ec7b004SRick Macklem  * shared lock and the NFSXXX_LOCK flag permits an exclusive lock.
23579ec7b004SRick Macklem  * The first argument is a pointer to an nfsv4lock structure.
23589ec7b004SRick Macklem  * The second argument is 1 iff a blocking lock is wanted.
23599ec7b004SRick Macklem  * If this argument is 0, the call waits until no thread either wants nor
23609ec7b004SRick Macklem  * holds an exclusive lock.
23619ec7b004SRick Macklem  * It returns 1 if the lock was acquired, 0 otherwise.
23629ec7b004SRick Macklem  * If several processes call this function concurrently wanting the exclusive
23639ec7b004SRick Macklem  * lock, one will get the lock and the rest will return without getting the
23649ec7b004SRick Macklem  * lock. (If the caller must have the lock, it simply calls this function in a
23659ec7b004SRick Macklem  *  loop until the function returns 1 to indicate the lock was acquired.)
23669ec7b004SRick Macklem  * Any usecnt must be decremented by calling nfsv4_relref() before
23679ec7b004SRick Macklem  * calling nfsv4_lock(). It was done this way, so nfsv4_lock() could
23689ec7b004SRick Macklem  * be called in a loop.
2369ff29f3b2SRick Macklem  * The isleptp argument is set to indicate if the call slept, iff not NULL
2370ff29f3b2SRick Macklem  * and the mp argument indicates to check for a forced dismount, iff not
2371ff29f3b2SRick Macklem  * NULL.
23729ec7b004SRick Macklem  */
2373b9cc3262SRyan Moeller int
nfsv4_lock(struct nfsv4lock * lp,int iwantlock,int * isleptp,struct mtx * mutex,struct mount * mp)23749ec7b004SRick Macklem nfsv4_lock(struct nfsv4lock *lp, int iwantlock, int *isleptp,
23755f742d38SRick Macklem     struct mtx *mutex, struct mount *mp)
23769ec7b004SRick Macklem {
23779ec7b004SRick Macklem 
23789ec7b004SRick Macklem 	if (isleptp)
23799ec7b004SRick Macklem 		*isleptp = 0;
23809ec7b004SRick Macklem 	/*
23819ec7b004SRick Macklem 	 * If a lock is wanted, loop around until the lock is acquired by
23829ec7b004SRick Macklem 	 * someone and then released. If I want the lock, try to acquire it.
23839ec7b004SRick Macklem 	 * For a lock to be issued, no lock must be in force and the usecnt
23849ec7b004SRick Macklem 	 * must be zero.
23859ec7b004SRick Macklem 	 */
23869ec7b004SRick Macklem 	if (iwantlock) {
23879ec7b004SRick Macklem 	    if (!(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
23889ec7b004SRick Macklem 		lp->nfslock_usecnt == 0) {
23899ec7b004SRick Macklem 		lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
23909ec7b004SRick Macklem 		lp->nfslock_lock |= NFSV4LOCK_LOCK;
23919ec7b004SRick Macklem 		return (1);
23929ec7b004SRick Macklem 	    }
23939ec7b004SRick Macklem 	    lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED;
23949ec7b004SRick Macklem 	}
23959ec7b004SRick Macklem 	while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) {
239616f300faSRick Macklem 		if (mp != NULL && NFSCL_FORCEDISM(mp)) {
2397ff29f3b2SRick Macklem 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
2398ff29f3b2SRick Macklem 			return (0);
2399ff29f3b2SRick Macklem 		}
24009ec7b004SRick Macklem 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
24019ec7b004SRick Macklem 		if (isleptp)
24029ec7b004SRick Macklem 			*isleptp = 1;
24035f742d38SRick Macklem 		msleep(&lp->nfslock_lock, mutex, PVFS, "nfsv4lck", hz);
24049ec7b004SRick Macklem 		if (iwantlock && !(lp->nfslock_lock & NFSV4LOCK_LOCK) &&
24059ec7b004SRick Macklem 		    lp->nfslock_usecnt == 0) {
24069ec7b004SRick Macklem 			lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED;
24079ec7b004SRick Macklem 			lp->nfslock_lock |= NFSV4LOCK_LOCK;
24089ec7b004SRick Macklem 			return (1);
24099ec7b004SRick Macklem 		}
24109ec7b004SRick Macklem 	}
24119ec7b004SRick Macklem 	return (0);
24129ec7b004SRick Macklem }
24139ec7b004SRick Macklem 
24149ec7b004SRick Macklem /*
24159ec7b004SRick Macklem  * Release the lock acquired by nfsv4_lock().
24169ec7b004SRick Macklem  * The second argument is set to 1 to indicate the nfslock_usecnt should be
24179ec7b004SRick Macklem  * incremented, as well.
24189ec7b004SRick Macklem  */
2419b9cc3262SRyan Moeller void
nfsv4_unlock(struct nfsv4lock * lp,int incref)24209ec7b004SRick Macklem nfsv4_unlock(struct nfsv4lock *lp, int incref)
24219ec7b004SRick Macklem {
24229ec7b004SRick Macklem 
24239ec7b004SRick Macklem 	lp->nfslock_lock &= ~NFSV4LOCK_LOCK;
24249ec7b004SRick Macklem 	if (incref)
24259ec7b004SRick Macklem 		lp->nfslock_usecnt++;
24269ec7b004SRick Macklem 	nfsv4_wanted(lp);
24279ec7b004SRick Macklem }
24289ec7b004SRick Macklem 
24299ec7b004SRick Macklem /*
24309ec7b004SRick Macklem  * Release a reference cnt.
24319ec7b004SRick Macklem  */
2432b9cc3262SRyan Moeller void
nfsv4_relref(struct nfsv4lock * lp)24339ec7b004SRick Macklem nfsv4_relref(struct nfsv4lock *lp)
24349ec7b004SRick Macklem {
24359ec7b004SRick Macklem 
24369ec7b004SRick Macklem 	if (lp->nfslock_usecnt <= 0)
24379ec7b004SRick Macklem 		panic("nfsv4root ref cnt");
24389ec7b004SRick Macklem 	lp->nfslock_usecnt--;
24399ec7b004SRick Macklem 	if (lp->nfslock_usecnt == 0)
24409ec7b004SRick Macklem 		nfsv4_wanted(lp);
24419ec7b004SRick Macklem }
24429ec7b004SRick Macklem 
24439ec7b004SRick Macklem /*
24449ec7b004SRick Macklem  * Get a reference cnt.
24459ec7b004SRick Macklem  * This function will wait for any exclusive lock to be released, but will
24469ec7b004SRick Macklem  * not wait for threads that want the exclusive lock. If priority needs
24479ec7b004SRick Macklem  * to be given to threads that need the exclusive lock, a call to nfsv4_lock()
24489ec7b004SRick Macklem  * with the 2nd argument == 0 should be done before calling nfsv4_getref().
244916f300faSRick Macklem  * If the mp argument is not NULL, check for NFSCL_FORCEDISM() being set and
2450ff29f3b2SRick Macklem  * return without getting a refcnt for that case.
24519ec7b004SRick Macklem  */
2452b9cc3262SRyan Moeller void
nfsv4_getref(struct nfsv4lock * lp,int * isleptp,struct mtx * mutex,struct mount * mp)24535f742d38SRick Macklem nfsv4_getref(struct nfsv4lock *lp, int *isleptp, struct mtx *mutex,
2454ff29f3b2SRick Macklem     struct mount *mp)
24559ec7b004SRick Macklem {
24569ec7b004SRick Macklem 
24579ec7b004SRick Macklem 	if (isleptp)
24589ec7b004SRick Macklem 		*isleptp = 0;
24599ec7b004SRick Macklem 
24609ec7b004SRick Macklem 	/*
24619ec7b004SRick Macklem 	 * Wait for a lock held.
24629ec7b004SRick Macklem 	 */
24639ec7b004SRick Macklem 	while (lp->nfslock_lock & NFSV4LOCK_LOCK) {
246416f300faSRick Macklem 		if (mp != NULL && NFSCL_FORCEDISM(mp))
2465ff29f3b2SRick Macklem 			return;
24669ec7b004SRick Macklem 		lp->nfslock_lock |= NFSV4LOCK_WANTED;
24679ec7b004SRick Macklem 		if (isleptp)
24689ec7b004SRick Macklem 			*isleptp = 1;
24695f742d38SRick Macklem 		msleep(&lp->nfslock_lock, mutex, PVFS, "nfsv4gr", hz);
24709ec7b004SRick Macklem 	}
247116f300faSRick Macklem 	if (mp != NULL && NFSCL_FORCEDISM(mp))
2472ff29f3b2SRick Macklem 		return;
24739ec7b004SRick Macklem 
24749ec7b004SRick Macklem 	lp->nfslock_usecnt++;
24759ec7b004SRick Macklem }
24769ec7b004SRick Macklem 
24779ec7b004SRick Macklem /*
24782ec3f925SRick Macklem  * Get a reference as above, but return failure instead of sleeping if
24792ec3f925SRick Macklem  * an exclusive lock is held.
24802ec3f925SRick Macklem  */
2481b9cc3262SRyan Moeller int
nfsv4_getref_nonblock(struct nfsv4lock * lp)24822ec3f925SRick Macklem nfsv4_getref_nonblock(struct nfsv4lock *lp)
24832ec3f925SRick Macklem {
24842ec3f925SRick Macklem 
24852ec3f925SRick Macklem 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) != 0)
24862ec3f925SRick Macklem 		return (0);
24872ec3f925SRick Macklem 
24882ec3f925SRick Macklem 	lp->nfslock_usecnt++;
24892ec3f925SRick Macklem 	return (1);
24902ec3f925SRick Macklem }
24912ec3f925SRick Macklem 
24922ec3f925SRick Macklem /*
2493a43fcbe3SRick Macklem  * Test for a lock. Return 1 if locked, 0 otherwise.
2494a43fcbe3SRick Macklem  */
2495b9cc3262SRyan Moeller int
nfsv4_testlock(struct nfsv4lock * lp)2496a43fcbe3SRick Macklem nfsv4_testlock(struct nfsv4lock *lp)
2497a43fcbe3SRick Macklem {
2498a43fcbe3SRick Macklem 
2499a43fcbe3SRick Macklem 	if ((lp->nfslock_lock & NFSV4LOCK_LOCK) == 0 &&
2500a43fcbe3SRick Macklem 	    lp->nfslock_usecnt == 0)
2501a43fcbe3SRick Macklem 		return (0);
2502a43fcbe3SRick Macklem 	return (1);
2503a43fcbe3SRick Macklem }
2504a43fcbe3SRick Macklem 
2505a43fcbe3SRick Macklem /*
25069ec7b004SRick Macklem  * Wake up anyone sleeping, waiting for this lock.
25079ec7b004SRick Macklem  */
25089ec7b004SRick Macklem static void
nfsv4_wanted(struct nfsv4lock * lp)25099ec7b004SRick Macklem nfsv4_wanted(struct nfsv4lock *lp)
25109ec7b004SRick Macklem {
25119ec7b004SRick Macklem 
25129ec7b004SRick Macklem 	if (lp->nfslock_lock & NFSV4LOCK_WANTED) {
25139ec7b004SRick Macklem 		lp->nfslock_lock &= ~NFSV4LOCK_WANTED;
25149ec7b004SRick Macklem 		wakeup((caddr_t)&lp->nfslock_lock);
25159ec7b004SRick Macklem 	}
25169ec7b004SRick Macklem }
25179ec7b004SRick Macklem 
25189ec7b004SRick Macklem /*
25199ec7b004SRick Macklem  * Copy a string from an mbuf list into a character array.
25209ec7b004SRick Macklem  * Return EBADRPC if there is an mbuf error,
25219ec7b004SRick Macklem  * 0 otherwise.
25229ec7b004SRick Macklem  */
2523b9cc3262SRyan Moeller int
nfsrv_mtostr(struct nfsrv_descript * nd,char * str,int siz)25249ec7b004SRick Macklem nfsrv_mtostr(struct nfsrv_descript *nd, char *str, int siz)
25259ec7b004SRick Macklem {
25269ec7b004SRick Macklem 	char *cp;
25279ec7b004SRick Macklem 	int xfer, len;
2528ae070589SRick Macklem 	struct mbuf *mp;
25299ec7b004SRick Macklem 	int rem, error = 0;
25309ec7b004SRick Macklem 
25319ec7b004SRick Macklem 	mp = nd->nd_md;
25329ec7b004SRick Macklem 	cp = nd->nd_dpos;
2533c948a17aSRick Macklem 	len = mtod(mp, caddr_t) + mp->m_len - cp;
25349ec7b004SRick Macklem 	rem = NFSM_RNDUP(siz) - siz;
25359ec7b004SRick Macklem 	while (siz > 0) {
25369ec7b004SRick Macklem 		if (len > siz)
25379ec7b004SRick Macklem 			xfer = siz;
25389ec7b004SRick Macklem 		else
25399ec7b004SRick Macklem 			xfer = len;
25409ec7b004SRick Macklem 		NFSBCOPY(cp, str, xfer);
25419ec7b004SRick Macklem 		str += xfer;
25429ec7b004SRick Macklem 		siz -= xfer;
25439ec7b004SRick Macklem 		if (siz > 0) {
2544c948a17aSRick Macklem 			mp = mp->m_next;
2545a9285ae5SZack Kirsch 			if (mp == NULL) {
2546a9285ae5SZack Kirsch 				error = EBADRPC;
2547a9285ae5SZack Kirsch 				goto out;
2548a9285ae5SZack Kirsch 			}
2549c948a17aSRick Macklem 			cp = mtod(mp, caddr_t);
2550c948a17aSRick Macklem 			len = mp->m_len;
25519ec7b004SRick Macklem 		} else {
25529ec7b004SRick Macklem 			cp += xfer;
25539ec7b004SRick Macklem 			len -= xfer;
25549ec7b004SRick Macklem 		}
25559ec7b004SRick Macklem 	}
25569ec7b004SRick Macklem 	*str = '\0';
25579ec7b004SRick Macklem 	nd->nd_dpos = cp;
25589ec7b004SRick Macklem 	nd->nd_md = mp;
25599ec7b004SRick Macklem 	if (rem > 0) {
25609ec7b004SRick Macklem 		if (len < rem)
25619ec7b004SRick Macklem 			error = nfsm_advance(nd, rem, len);
25629ec7b004SRick Macklem 		else
25639ec7b004SRick Macklem 			nd->nd_dpos += rem;
25649ec7b004SRick Macklem 	}
2565a9285ae5SZack Kirsch 
2566a9285ae5SZack Kirsch out:
2567a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
25689ec7b004SRick Macklem 	return (error);
25699ec7b004SRick Macklem }
25709ec7b004SRick Macklem 
25719ec7b004SRick Macklem /*
25729ec7b004SRick Macklem  * Fill in the attributes as marked by the bitmap (V4).
25739ec7b004SRick Macklem  */
2574b9cc3262SRyan Moeller int
nfsv4_fillattr(struct nfsrv_descript * nd,struct mount * mp,vnode_t vp,NFSACL_T * saclp,struct vattr * vap,fhandle_t * fhp,int rderror,nfsattrbit_t * attrbitp,struct ucred * cred,NFSPROC_T * p,int isdgram,int reterr,int supports_nfsv4acls,int at_root,uint64_t mounted_on_fileno,struct statfs * pnfssf)257507c0c166SRick Macklem nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
257607c0c166SRick Macklem     NFSACL_T *saclp, struct vattr *vap, fhandle_t *fhp, int rderror,
257707c0c166SRick Macklem     nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
257890d2dfabSRick Macklem     int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno,
257990d2dfabSRick Macklem     struct statfs *pnfssf)
25809ec7b004SRick Macklem {
25819ec7b004SRick Macklem 	int bitpos, retnum = 0;
25829ec7b004SRick Macklem 	u_int32_t *tl;
25839ec7b004SRick Macklem 	int siz, prefixnum, error;
25849ec7b004SRick Macklem 	u_char *cp, namestr[NFSV4_SMALLSTR];
25859ec7b004SRick Macklem 	nfsattrbit_t attrbits, retbits;
25869ec7b004SRick Macklem 	nfsattrbit_t *retbitp = &retbits;
25879ec7b004SRick Macklem 	u_int32_t freenum, *retnump;
25889ec7b004SRick Macklem 	u_int64_t uquad;
25892f304845SKonstantin Belousov 	struct statfs *fs;
25909ec7b004SRick Macklem 	struct nfsfsinfo fsinf;
25919ec7b004SRick Macklem 	struct timespec temptime;
25929ec7b004SRick Macklem 	NFSACL_T *aclp, *naclp = NULL;
2593c057a378SRick Macklem 	size_t atsiz;
2594c057a378SRick Macklem 	bool xattrsupp;
25959ec7b004SRick Macklem #ifdef QUOTA
25969ec7b004SRick Macklem 	struct dqblk dqb;
25979ec7b004SRick Macklem 	uid_t savuid;
25989ec7b004SRick Macklem #endif
25999ec7b004SRick Macklem 
26009ec7b004SRick Macklem 	/*
26019ec7b004SRick Macklem 	 * First, set the bits that can be filled and get fsinfo.
26029ec7b004SRick Macklem 	 */
26039ec7b004SRick Macklem 	NFSSET_ATTRBIT(retbitp, attrbitp);
2604b921158aSRick Macklem 	/*
2605b921158aSRick Macklem 	 * If both p and cred are NULL, it is a client side setattr call.
2606b921158aSRick Macklem 	 * If both p and cred are not NULL, it is a server side reply call.
2607b921158aSRick Macklem 	 * If p is not NULL and cred is NULL, it is a client side callback
2608b921158aSRick Macklem 	 * reply call.
2609b921158aSRick Macklem 	 */
26109ec7b004SRick Macklem 	if (p == NULL && cred == NULL) {
2611ea5776ecSRick Macklem 		NFSCLRNOTSETABLE_ATTRBIT(retbitp, nd);
26129ec7b004SRick Macklem 		aclp = saclp;
26139ec7b004SRick Macklem 	} else {
2614ea5776ecSRick Macklem 		NFSCLRNOTFILLABLE_ATTRBIT(retbitp, nd);
2615c3e22f83SRick Macklem 		naclp = acl_alloc(M_WAITOK);
26169ec7b004SRick Macklem 		aclp = naclp;
26179ec7b004SRick Macklem 	}
26189ec7b004SRick Macklem 	nfsvno_getfs(&fsinf, isdgram);
26199ec7b004SRick Macklem 	/*
26209ec7b004SRick Macklem 	 * Get the VFS_STATFS(), since some attributes need them.
26219ec7b004SRick Macklem 	 */
26222f304845SKonstantin Belousov 	fs = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
26239ec7b004SRick Macklem 	if (NFSISSETSTATFS_ATTRBIT(retbitp)) {
26242f304845SKonstantin Belousov 		error = VFS_STATFS(mp, fs);
26259ec7b004SRick Macklem 		if (error != 0) {
26269ec7b004SRick Macklem 			if (reterr) {
26279ec7b004SRick Macklem 				nd->nd_repstat = NFSERR_ACCES;
26282f304845SKonstantin Belousov 				free(fs, M_STATFS);
26299ec7b004SRick Macklem 				return (0);
26309ec7b004SRick Macklem 			}
26319ec7b004SRick Macklem 			NFSCLRSTATFS_ATTRBIT(retbitp);
26329ec7b004SRick Macklem 		}
2633d70ca5b0SRick Macklem 		/*
2634d70ca5b0SRick Macklem 		 * Since NFS handles these values as unsigned on the
2635d70ca5b0SRick Macklem 		 * wire, there is no way to represent negative values,
2636d70ca5b0SRick Macklem 		 * so set them to 0. Without this, they will appear
2637d70ca5b0SRick Macklem 		 * to be very large positive values for clients like
2638d70ca5b0SRick Macklem 		 * Solaris10.
2639d70ca5b0SRick Macklem 		 */
2640d70ca5b0SRick Macklem 		if (fs->f_bavail < 0)
2641d70ca5b0SRick Macklem 			fs->f_bavail = 0;
2642d70ca5b0SRick Macklem 		if (fs->f_ffree < 0)
2643d70ca5b0SRick Macklem 			fs->f_ffree = 0;
26449ec7b004SRick Macklem 	}
26459ec7b004SRick Macklem 
26469ec7b004SRick Macklem 	/*
26479ec7b004SRick Macklem 	 * And the NFSv4 ACL...
26489ec7b004SRick Macklem 	 */
264974991298SEdward Tomasz Napierala 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT) &&
265074991298SEdward Tomasz Napierala 	    (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2651a09001a8SRick Macklem 		supports_nfsv4acls == 0))) {
26529ec7b004SRick Macklem 		NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACLSUPPORT);
26539ec7b004SRick Macklem 	}
26549ec7b004SRick Macklem 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_ACL)) {
26559ec7b004SRick Macklem 		if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL) &&
2656a09001a8SRick Macklem 		    supports_nfsv4acls == 0)) {
26579ec7b004SRick Macklem 			NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
26589ec7b004SRick Macklem 		} else if (naclp != NULL) {
265998f234f3SZack Kirsch 			if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
26608207db3eSRick Macklem 				error = VOP_ACCESSX(vp, VREAD_ACL, cred, p);
26619ec7b004SRick Macklem 				if (error == 0)
266217891d00SRick Macklem 					error = VOP_GETACL(vp, ACL_TYPE_NFS4,
266317891d00SRick Macklem 					    naclp, cred, p);
2664b249ce48SMateusz Guzik 				NFSVOPUNLOCK(vp);
266517891d00SRick Macklem 			} else
266617891d00SRick Macklem 				error = NFSERR_PERM;
26679ec7b004SRick Macklem 			if (error != 0) {
26689ec7b004SRick Macklem 				if (reterr) {
26699ec7b004SRick Macklem 					nd->nd_repstat = NFSERR_ACCES;
26702f304845SKonstantin Belousov 					free(fs, M_STATFS);
26719ec7b004SRick Macklem 					return (0);
26729ec7b004SRick Macklem 				}
26739ec7b004SRick Macklem 				NFSCLRBIT_ATTRBIT(retbitp, NFSATTRBIT_ACL);
26749ec7b004SRick Macklem 			}
26759ec7b004SRick Macklem 		}
26769ec7b004SRick Macklem 	}
2677d8a5961fSMarcelo Araujo 
2678c057a378SRick Macklem 	/* Check to see if Extended Attributes are supported. */
2679c057a378SRick Macklem 	xattrsupp = false;
2680c057a378SRick Macklem 	if (NFSISSET_ATTRBIT(retbitp, NFSATTRBIT_XATTRSUPPORT)) {
2681c057a378SRick Macklem 		if (NFSVOPLOCK(vp, LK_SHARED) == 0) {
2682c057a378SRick Macklem 			error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER,
2683c057a378SRick Macklem 			    "xxx", NULL, &atsiz, cred, p);
2684b249ce48SMateusz Guzik 			NFSVOPUNLOCK(vp);
2685c057a378SRick Macklem 			if (error != EOPNOTSUPP)
2686c057a378SRick Macklem 				xattrsupp = true;
2687c057a378SRick Macklem 		}
2688c057a378SRick Macklem 	}
2689c057a378SRick Macklem 
26909ec7b004SRick Macklem 	/*
26919ec7b004SRick Macklem 	 * Put out the attribute bitmap for the ones being filled in
26929ec7b004SRick Macklem 	 * and get the field for the number of attributes returned.
26939ec7b004SRick Macklem 	 */
26949ec7b004SRick Macklem 	prefixnum = nfsrv_putattrbit(nd, retbitp);
26959ec7b004SRick Macklem 	NFSM_BUILD(retnump, u_int32_t *, NFSX_UNSIGNED);
26969ec7b004SRick Macklem 	prefixnum += NFSX_UNSIGNED;
26979ec7b004SRick Macklem 
26989ec7b004SRick Macklem 	/*
26999ec7b004SRick Macklem 	 * Now, loop around filling in the attributes for each bit set.
27009ec7b004SRick Macklem 	 */
27019ec7b004SRick Macklem 	for (bitpos = 0; bitpos < NFSATTRBIT_MAX; bitpos++) {
27029ec7b004SRick Macklem 	    if (NFSISSET_ATTRBIT(retbitp, bitpos)) {
27039ec7b004SRick Macklem 		switch (bitpos) {
27049ec7b004SRick Macklem 		case NFSATTRBIT_SUPPORTEDATTRS:
2705ea5776ecSRick Macklem 			NFSSETSUPP_ATTRBIT(&attrbits, nd);
27069ec7b004SRick Macklem 			if (nfsrv_useacl == 0 || ((cred != NULL || p != NULL)
2707a09001a8SRick Macklem 			    && supports_nfsv4acls == 0)) {
27089ec7b004SRick Macklem 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACLSUPPORT);
27099ec7b004SRick Macklem 			    NFSCLRBIT_ATTRBIT(&attrbits,NFSATTRBIT_ACL);
27109ec7b004SRick Macklem 			}
27119ec7b004SRick Macklem 			retnum += nfsrv_putattrbit(nd, &attrbits);
27129ec7b004SRick Macklem 			break;
27139ec7b004SRick Macklem 		case NFSATTRBIT_TYPE:
27149ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27159ec7b004SRick Macklem 			*tl = vtonfsv34_type(vap->va_type);
27169ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27179ec7b004SRick Macklem 			break;
27189ec7b004SRick Macklem 		case NFSATTRBIT_FHEXPIRETYPE:
27199ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27209ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4FHTYPE_PERSISTENT);
27219ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27229ec7b004SRick Macklem 			break;
27239ec7b004SRick Macklem 		case NFSATTRBIT_CHANGE:
27249ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
27259ec7b004SRick Macklem 			txdr_hyper(vap->va_filerev, tl);
27269ec7b004SRick Macklem 			retnum += NFSX_HYPER;
27279ec7b004SRick Macklem 			break;
27289ec7b004SRick Macklem 		case NFSATTRBIT_SIZE:
27299ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
27309ec7b004SRick Macklem 			txdr_hyper(vap->va_size, tl);
27319ec7b004SRick Macklem 			retnum += NFSX_HYPER;
27329ec7b004SRick Macklem 			break;
27339ec7b004SRick Macklem 		case NFSATTRBIT_LINKSUPPORT:
27349ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27359ec7b004SRick Macklem 			if (fsinf.fs_properties & NFSV3FSINFO_LINK)
27369ec7b004SRick Macklem 				*tl = newnfs_true;
27379ec7b004SRick Macklem 			else
27389ec7b004SRick Macklem 				*tl = newnfs_false;
27399ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27409ec7b004SRick Macklem 			break;
27419ec7b004SRick Macklem 		case NFSATTRBIT_SYMLINKSUPPORT:
27429ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27439ec7b004SRick Macklem 			if (fsinf.fs_properties & NFSV3FSINFO_SYMLINK)
27449ec7b004SRick Macklem 				*tl = newnfs_true;
27459ec7b004SRick Macklem 			else
27469ec7b004SRick Macklem 				*tl = newnfs_false;
27479ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27489ec7b004SRick Macklem 			break;
27499ec7b004SRick Macklem 		case NFSATTRBIT_NAMEDATTR:
27509ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27519ec7b004SRick Macklem 			*tl = newnfs_false;
27529ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27539ec7b004SRick Macklem 			break;
27549ec7b004SRick Macklem 		case NFSATTRBIT_FSID:
27559ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4FSID);
27569ec7b004SRick Macklem 			*tl++ = 0;
275707c0c166SRick Macklem 			*tl++ = txdr_unsigned(mp->mnt_stat.f_fsid.val[0]);
27589ec7b004SRick Macklem 			*tl++ = 0;
275907c0c166SRick Macklem 			*tl = txdr_unsigned(mp->mnt_stat.f_fsid.val[1]);
27609ec7b004SRick Macklem 			retnum += NFSX_V4FSID;
27619ec7b004SRick Macklem 			break;
27629ec7b004SRick Macklem 		case NFSATTRBIT_UNIQUEHANDLES:
27639ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27649ec7b004SRick Macklem 			*tl = newnfs_true;
27659ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27669ec7b004SRick Macklem 			break;
27679ec7b004SRick Macklem 		case NFSATTRBIT_LEASETIME:
27689ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27699ec7b004SRick Macklem 			*tl = txdr_unsigned(nfsrv_lease);
27709ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27719ec7b004SRick Macklem 			break;
27729ec7b004SRick Macklem 		case NFSATTRBIT_RDATTRERROR:
27739ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27749ec7b004SRick Macklem 			*tl = txdr_unsigned(rderror);
27759ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27769ec7b004SRick Macklem 			break;
27779ec7b004SRick Macklem 		/*
27789ec7b004SRick Macklem 		 * Recommended Attributes. (Only the supported ones.)
27799ec7b004SRick Macklem 		 */
27809ec7b004SRick Macklem 		case NFSATTRBIT_ACL:
27811ebc14c9SRick Macklem 			retnum += nfsrv_buildacl(nd, aclp, vp->v_type, p);
27829ec7b004SRick Macklem 			break;
27839ec7b004SRick Macklem 		case NFSATTRBIT_ACLSUPPORT:
27849ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27859ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSV4ACE_SUPTYPES);
27869ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27879ec7b004SRick Macklem 			break;
27889ec7b004SRick Macklem 		case NFSATTRBIT_CANSETTIME:
27899ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27909ec7b004SRick Macklem 			if (fsinf.fs_properties & NFSV3FSINFO_CANSETTIME)
27919ec7b004SRick Macklem 				*tl = newnfs_true;
27929ec7b004SRick Macklem 			else
27939ec7b004SRick Macklem 				*tl = newnfs_false;
27949ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
27959ec7b004SRick Macklem 			break;
27969ec7b004SRick Macklem 		case NFSATTRBIT_CASEINSENSITIVE:
27979ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
27989ec7b004SRick Macklem 			*tl = newnfs_false;
27999ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28009ec7b004SRick Macklem 			break;
28019ec7b004SRick Macklem 		case NFSATTRBIT_CASEPRESERVING:
28029ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
28039ec7b004SRick Macklem 			*tl = newnfs_true;
28049ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28059ec7b004SRick Macklem 			break;
28069ec7b004SRick Macklem 		case NFSATTRBIT_CHOWNRESTRICTED:
28079ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
2808061c683cSZack Kirsch 			*tl = newnfs_true;
28099ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28109ec7b004SRick Macklem 			break;
28119ec7b004SRick Macklem 		case NFSATTRBIT_FILEHANDLE:
2812896516e5SRick Macklem 			retnum += nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 0);
28139ec7b004SRick Macklem 			break;
28149ec7b004SRick Macklem 		case NFSATTRBIT_FILEID:
28159ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
281695ac7f1aSRick Macklem 			uquad = vap->va_fileid;
281795ac7f1aSRick Macklem 			txdr_hyper(uquad, tl);
28189ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28199ec7b004SRick Macklem 			break;
28209ec7b004SRick Macklem 		case NFSATTRBIT_FILESAVAIL:
28212d90ef47SRick Macklem 			freenum = nfsv4_filesavail(fs, mp);
28229ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
28239ec7b004SRick Macklem 			*tl++ = 0;
28249ec7b004SRick Macklem 			*tl = txdr_unsigned(freenum);
28259ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28269ec7b004SRick Macklem 			break;
28279ec7b004SRick Macklem 		case NFSATTRBIT_FILESFREE:
28289ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
28299ec7b004SRick Macklem 			*tl++ = 0;
28302f304845SKonstantin Belousov 			*tl = txdr_unsigned(fs->f_ffree);
28319ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28329ec7b004SRick Macklem 			break;
28339ec7b004SRick Macklem 		case NFSATTRBIT_FILESTOTAL:
28349ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
28359ec7b004SRick Macklem 			*tl++ = 0;
28362f304845SKonstantin Belousov 			*tl = txdr_unsigned(fs->f_files);
28379ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28389ec7b004SRick Macklem 			break;
28399ec7b004SRick Macklem 		case NFSATTRBIT_FSLOCATIONS:
28409ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
28419ec7b004SRick Macklem 			*tl++ = 0;
28429ec7b004SRick Macklem 			*tl = 0;
28439ec7b004SRick Macklem 			retnum += 2 * NFSX_UNSIGNED;
28449ec7b004SRick Macklem 			break;
28459ec7b004SRick Macklem 		case NFSATTRBIT_HOMOGENEOUS:
28469ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
28479ec7b004SRick Macklem 			if (fsinf.fs_properties & NFSV3FSINFO_HOMOGENEOUS)
28489ec7b004SRick Macklem 				*tl = newnfs_true;
28499ec7b004SRick Macklem 			else
28509ec7b004SRick Macklem 				*tl = newnfs_false;
28519ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28529ec7b004SRick Macklem 			break;
28539ec7b004SRick Macklem 		case NFSATTRBIT_MAXFILESIZE:
28549ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
28559ec7b004SRick Macklem 			uquad = NFSRV_MAXFILESIZE;
28569ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
28579ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28589ec7b004SRick Macklem 			break;
28599ec7b004SRick Macklem 		case NFSATTRBIT_MAXLINK:
28609ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
286155384243SJohn Baldwin 			*tl = txdr_unsigned(NFS_LINK_MAX);
28629ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28639ec7b004SRick Macklem 			break;
28649ec7b004SRick Macklem 		case NFSATTRBIT_MAXNAME:
28659ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
28669ec7b004SRick Macklem 			*tl = txdr_unsigned(NFS_MAXNAMLEN);
28679ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28689ec7b004SRick Macklem 			break;
28699ec7b004SRick Macklem 		case NFSATTRBIT_MAXREAD:
28709ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
28719ec7b004SRick Macklem 			*tl++ = 0;
28729ec7b004SRick Macklem 			*tl = txdr_unsigned(fsinf.fs_rtmax);
28739ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28749ec7b004SRick Macklem 			break;
28759ec7b004SRick Macklem 		case NFSATTRBIT_MAXWRITE:
28769ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
28779ec7b004SRick Macklem 			*tl++ = 0;
28789ec7b004SRick Macklem 			*tl = txdr_unsigned(fsinf.fs_wtmax);
28799ec7b004SRick Macklem 			retnum += NFSX_HYPER;
28809ec7b004SRick Macklem 			break;
28819ec7b004SRick Macklem 		case NFSATTRBIT_MODE:
28829ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
28839ec7b004SRick Macklem 			*tl = vtonfsv34_mode(vap->va_mode);
28849ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28859ec7b004SRick Macklem 			break;
28869ec7b004SRick Macklem 		case NFSATTRBIT_NOTRUNC:
28879ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
28889ec7b004SRick Macklem 			*tl = newnfs_true;
28899ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28909ec7b004SRick Macklem 			break;
28919ec7b004SRick Macklem 		case NFSATTRBIT_NUMLINKS:
28929ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
28939ec7b004SRick Macklem 			*tl = txdr_unsigned(vap->va_nlink);
28949ec7b004SRick Macklem 			retnum += NFSX_UNSIGNED;
28959ec7b004SRick Macklem 			break;
28969ec7b004SRick Macklem 		case NFSATTRBIT_OWNER:
28979ec7b004SRick Macklem 			cp = namestr;
28980f86b94aSEdward Tomasz Napierala 			nfsv4_uidtostr(vap->va_uid, &cp, &siz);
28999ec7b004SRick Macklem 			retnum += nfsm_strtom(nd, cp, siz);
29009ec7b004SRick Macklem 			if (cp != namestr)
29019ec7b004SRick Macklem 				free(cp, M_NFSSTRING);
29029ec7b004SRick Macklem 			break;
29039ec7b004SRick Macklem 		case NFSATTRBIT_OWNERGROUP:
29049ec7b004SRick Macklem 			cp = namestr;
2905c703cba8SEdward Tomasz Napierala 			nfsv4_gidtostr(vap->va_gid, &cp, &siz);
29069ec7b004SRick Macklem 			retnum += nfsm_strtom(nd, cp, siz);
29079ec7b004SRick Macklem 			if (cp != namestr)
29089ec7b004SRick Macklem 				free(cp, M_NFSSTRING);
29099ec7b004SRick Macklem 			break;
29109ec7b004SRick Macklem 		case NFSATTRBIT_QUOTAHARD:
2911cc426dd3SMateusz Guzik 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA))
29122f304845SKonstantin Belousov 				freenum = fs->f_bfree;
29139ec7b004SRick Macklem 			else
29142f304845SKonstantin Belousov 				freenum = fs->f_bavail;
29159ec7b004SRick Macklem #ifdef QUOTA
29169ec7b004SRick Macklem 			/*
29179ec7b004SRick Macklem 			 * ufs_quotactl() insists that the uid argument
29189ec7b004SRick Macklem 			 * equal p_ruid for non-root quota access, so
29199ec7b004SRick Macklem 			 * we'll just make sure that's the case.
29209ec7b004SRick Macklem 			 */
29219ec7b004SRick Macklem 			savuid = p->p_cred->p_ruid;
29229ec7b004SRick Macklem 			p->p_cred->p_ruid = cred->cr_uid;
292307c0c166SRick Macklem 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
292452e63ec2SBrooks Davis 			    cred->cr_uid, &dqb))
29259ec7b004SRick Macklem 			    freenum = min(dqb.dqb_bhardlimit, freenum);
29269ec7b004SRick Macklem 			p->p_cred->p_ruid = savuid;
29279ec7b004SRick Macklem #endif	/* QUOTA */
29289ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
29299ec7b004SRick Macklem 			uquad = (u_int64_t)freenum;
29302f304845SKonstantin Belousov 			NFSQUOTABLKTOBYTE(uquad, fs->f_bsize);
29319ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
29329ec7b004SRick Macklem 			retnum += NFSX_HYPER;
29339ec7b004SRick Macklem 			break;
29349ec7b004SRick Macklem 		case NFSATTRBIT_QUOTASOFT:
2935cc426dd3SMateusz Guzik 			if (priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA))
29362f304845SKonstantin Belousov 				freenum = fs->f_bfree;
29379ec7b004SRick Macklem 			else
29382f304845SKonstantin Belousov 				freenum = fs->f_bavail;
29399ec7b004SRick Macklem #ifdef QUOTA
29409ec7b004SRick Macklem 			/*
29419ec7b004SRick Macklem 			 * ufs_quotactl() insists that the uid argument
29429ec7b004SRick Macklem 			 * equal p_ruid for non-root quota access, so
29439ec7b004SRick Macklem 			 * we'll just make sure that's the case.
29449ec7b004SRick Macklem 			 */
29459ec7b004SRick Macklem 			savuid = p->p_cred->p_ruid;
29469ec7b004SRick Macklem 			p->p_cred->p_ruid = cred->cr_uid;
294707c0c166SRick Macklem 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
294852e63ec2SBrooks Davis 			    cred->cr_uid, &dqb))
29499ec7b004SRick Macklem 			    freenum = min(dqb.dqb_bsoftlimit, freenum);
29509ec7b004SRick Macklem 			p->p_cred->p_ruid = savuid;
29519ec7b004SRick Macklem #endif	/* QUOTA */
29529ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
29539ec7b004SRick Macklem 			uquad = (u_int64_t)freenum;
29542f304845SKonstantin Belousov 			NFSQUOTABLKTOBYTE(uquad, fs->f_bsize);
29559ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
29569ec7b004SRick Macklem 			retnum += NFSX_HYPER;
29579ec7b004SRick Macklem 			break;
29589ec7b004SRick Macklem 		case NFSATTRBIT_QUOTAUSED:
29599ec7b004SRick Macklem 			freenum = 0;
29609ec7b004SRick Macklem #ifdef QUOTA
29619ec7b004SRick Macklem 			/*
29629ec7b004SRick Macklem 			 * ufs_quotactl() insists that the uid argument
29639ec7b004SRick Macklem 			 * equal p_ruid for non-root quota access, so
29649ec7b004SRick Macklem 			 * we'll just make sure that's the case.
29659ec7b004SRick Macklem 			 */
29669ec7b004SRick Macklem 			savuid = p->p_cred->p_ruid;
29679ec7b004SRick Macklem 			p->p_cred->p_ruid = cred->cr_uid;
296807c0c166SRick Macklem 			if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
296952e63ec2SBrooks Davis 			    cred->cr_uid, &dqb))
29709ec7b004SRick Macklem 			    freenum = dqb.dqb_curblocks;
29719ec7b004SRick Macklem 			p->p_cred->p_ruid = savuid;
29729ec7b004SRick Macklem #endif	/* QUOTA */
29739ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
29749ec7b004SRick Macklem 			uquad = (u_int64_t)freenum;
29752f304845SKonstantin Belousov 			NFSQUOTABLKTOBYTE(uquad, fs->f_bsize);
29769ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
29779ec7b004SRick Macklem 			retnum += NFSX_HYPER;
29789ec7b004SRick Macklem 			break;
29799ec7b004SRick Macklem 		case NFSATTRBIT_RAWDEV:
29809ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4SPECDATA);
29819ec7b004SRick Macklem 			*tl++ = txdr_unsigned(NFSMAJOR(vap->va_rdev));
29829ec7b004SRick Macklem 			*tl = txdr_unsigned(NFSMINOR(vap->va_rdev));
29839ec7b004SRick Macklem 			retnum += NFSX_V4SPECDATA;
29849ec7b004SRick Macklem 			break;
29859ec7b004SRick Macklem 		case NFSATTRBIT_SPACEAVAIL:
29869ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
2987cc426dd3SMateusz Guzik 			if (priv_check_cred(cred, PRIV_VFS_BLOCKRESERVE)) {
298890d2dfabSRick Macklem 				if (pnfssf != NULL)
298990d2dfabSRick Macklem 					uquad = (u_int64_t)pnfssf->f_bfree;
299090d2dfabSRick Macklem 				else
29912f304845SKonstantin Belousov 					uquad = (u_int64_t)fs->f_bfree;
299290d2dfabSRick Macklem 			} else {
299390d2dfabSRick Macklem 				if (pnfssf != NULL)
299490d2dfabSRick Macklem 					uquad = (u_int64_t)pnfssf->f_bavail;
29959ec7b004SRick Macklem 				else
29962f304845SKonstantin Belousov 					uquad = (u_int64_t)fs->f_bavail;
299790d2dfabSRick Macklem 			}
299890d2dfabSRick Macklem 			if (pnfssf != NULL)
299990d2dfabSRick Macklem 				uquad *= pnfssf->f_bsize;
300090d2dfabSRick Macklem 			else
30012f304845SKonstantin Belousov 				uquad *= fs->f_bsize;
30029ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
30039ec7b004SRick Macklem 			retnum += NFSX_HYPER;
30049ec7b004SRick Macklem 			break;
30059ec7b004SRick Macklem 		case NFSATTRBIT_SPACEFREE:
30069ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
300790d2dfabSRick Macklem 			if (pnfssf != NULL) {
300890d2dfabSRick Macklem 				uquad = (u_int64_t)pnfssf->f_bfree;
300990d2dfabSRick Macklem 				uquad *= pnfssf->f_bsize;
301090d2dfabSRick Macklem 			} else {
30112f304845SKonstantin Belousov 				uquad = (u_int64_t)fs->f_bfree;
30122f304845SKonstantin Belousov 				uquad *= fs->f_bsize;
301390d2dfabSRick Macklem 			}
30149ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
30159ec7b004SRick Macklem 			retnum += NFSX_HYPER;
30169ec7b004SRick Macklem 			break;
30179ec7b004SRick Macklem 		case NFSATTRBIT_SPACETOTAL:
30189ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
301990d2dfabSRick Macklem 			if (pnfssf != NULL) {
302090d2dfabSRick Macklem 				uquad = (u_int64_t)pnfssf->f_blocks;
302190d2dfabSRick Macklem 				uquad *= pnfssf->f_bsize;
302290d2dfabSRick Macklem 			} else {
30232f304845SKonstantin Belousov 				uquad = (u_int64_t)fs->f_blocks;
30242f304845SKonstantin Belousov 				uquad *= fs->f_bsize;
302590d2dfabSRick Macklem 			}
30269ec7b004SRick Macklem 			txdr_hyper(uquad, tl);
30279ec7b004SRick Macklem 			retnum += NFSX_HYPER;
30289ec7b004SRick Macklem 			break;
30299ec7b004SRick Macklem 		case NFSATTRBIT_SPACEUSED:
30309ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
30319ec7b004SRick Macklem 			txdr_hyper(vap->va_bytes, tl);
30329ec7b004SRick Macklem 			retnum += NFSX_HYPER;
30339ec7b004SRick Macklem 			break;
30349ec7b004SRick Macklem 		case NFSATTRBIT_TIMEACCESS:
30359ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
30369ec7b004SRick Macklem 			txdr_nfsv4time(&vap->va_atime, tl);
30379ec7b004SRick Macklem 			retnum += NFSX_V4TIME;
30389ec7b004SRick Macklem 			break;
30399ec7b004SRick Macklem 		case NFSATTRBIT_TIMEACCESSSET:
30405055536eSJohn Baldwin 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
30419ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
30429ec7b004SRick Macklem 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
30439ec7b004SRick Macklem 				txdr_nfsv4time(&vap->va_atime, tl);
30449ec7b004SRick Macklem 				retnum += NFSX_V4SETTIME;
30459ec7b004SRick Macklem 			} else {
30469ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
30479ec7b004SRick Macklem 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
30489ec7b004SRick Macklem 				retnum += NFSX_UNSIGNED;
30499ec7b004SRick Macklem 			}
30509ec7b004SRick Macklem 			break;
30519ec7b004SRick Macklem 		case NFSATTRBIT_TIMEDELTA:
30529ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
30539ec7b004SRick Macklem 			temptime.tv_sec = 0;
30549ec7b004SRick Macklem 			temptime.tv_nsec = 1000000000 / hz;
30559ec7b004SRick Macklem 			txdr_nfsv4time(&temptime, tl);
30569ec7b004SRick Macklem 			retnum += NFSX_V4TIME;
30579ec7b004SRick Macklem 			break;
30589ec7b004SRick Macklem 		case NFSATTRBIT_TIMEMETADATA:
30599ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
30609ec7b004SRick Macklem 			txdr_nfsv4time(&vap->va_ctime, tl);
30619ec7b004SRick Macklem 			retnum += NFSX_V4TIME;
30629ec7b004SRick Macklem 			break;
30639ec7b004SRick Macklem 		case NFSATTRBIT_TIMEMODIFY:
30649ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
30659ec7b004SRick Macklem 			txdr_nfsv4time(&vap->va_mtime, tl);
30669ec7b004SRick Macklem 			retnum += NFSX_V4TIME;
30679ec7b004SRick Macklem 			break;
30683900c114SDoug Rabson 		case NFSATTRBIT_TIMECREATE:
30693900c114SDoug Rabson 			NFSM_BUILD(tl, u_int32_t *, NFSX_V4TIME);
30703900c114SDoug Rabson 			txdr_nfsv4time(&vap->va_birthtime, tl);
30713900c114SDoug Rabson 			retnum += NFSX_V4TIME;
30723900c114SDoug Rabson 			break;
30739ec7b004SRick Macklem 		case NFSATTRBIT_TIMEMODIFYSET:
30745055536eSJohn Baldwin 			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
30759ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_V4SETTIME);
30769ec7b004SRick Macklem 				*tl++ = txdr_unsigned(NFSV4SATTRTIME_TOCLIENT);
30779ec7b004SRick Macklem 				txdr_nfsv4time(&vap->va_mtime, tl);
30789ec7b004SRick Macklem 				retnum += NFSX_V4SETTIME;
30799ec7b004SRick Macklem 			} else {
30809ec7b004SRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
30819ec7b004SRick Macklem 				*tl = txdr_unsigned(NFSV4SATTRTIME_TOSERVER);
30829ec7b004SRick Macklem 				retnum += NFSX_UNSIGNED;
30839ec7b004SRick Macklem 			}
30849ec7b004SRick Macklem 			break;
30859ec7b004SRick Macklem 		case NFSATTRBIT_MOUNTEDONFILEID:
30869ec7b004SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
308707c0c166SRick Macklem 			if (at_root != 0)
308807c0c166SRick Macklem 				uquad = mounted_on_fileno;
30899ec7b004SRick Macklem 			else
309095ac7f1aSRick Macklem 				uquad = vap->va_fileid;
309107c0c166SRick Macklem 			txdr_hyper(uquad, tl);
30929ec7b004SRick Macklem 			retnum += NFSX_HYPER;
30939ec7b004SRick Macklem 			break;
3094c59e4cc3SRick Macklem 		case NFSATTRBIT_SUPPATTREXCLCREAT:
3095ea5776ecSRick Macklem 			NFSSETSUPP_ATTRBIT(&attrbits, nd);
3096ea5776ecSRick Macklem 			NFSCLRNOTSETABLE_ATTRBIT(&attrbits, nd);
3097c59e4cc3SRick Macklem 			NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
3098c59e4cc3SRick Macklem 			retnum += nfsrv_putattrbit(nd, &attrbits);
3099c59e4cc3SRick Macklem 			break;
310090d2dfabSRick Macklem 		case NFSATTRBIT_FSLAYOUTTYPE:
310190d2dfabSRick Macklem 		case NFSATTRBIT_LAYOUTTYPE:
310290d2dfabSRick Macklem 			if (nfsrv_devidcnt == 0)
310390d2dfabSRick Macklem 				siz = 1;
310490d2dfabSRick Macklem 			else
310590d2dfabSRick Macklem 				siz = 2;
310690d2dfabSRick Macklem 			if (siz == 2) {
310790d2dfabSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
310890d2dfabSRick Macklem 				*tl++ = txdr_unsigned(1);	/* One entry. */
310990d2dfabSRick Macklem 				if (nfsrv_doflexfile != 0 ||
311090d2dfabSRick Macklem 				    nfsrv_maxpnfsmirror > 1)
311190d2dfabSRick Macklem 					*tl = txdr_unsigned(NFSLAYOUT_FLEXFILE);
311290d2dfabSRick Macklem 				else
311390d2dfabSRick Macklem 					*tl = txdr_unsigned(
311490d2dfabSRick Macklem 					    NFSLAYOUT_NFSV4_1_FILES);
311590d2dfabSRick Macklem 			} else {
311690d2dfabSRick Macklem 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
311790d2dfabSRick Macklem 				*tl = 0;
311890d2dfabSRick Macklem 			}
311990d2dfabSRick Macklem 			retnum += siz * NFSX_UNSIGNED;
312090d2dfabSRick Macklem 			break;
312190d2dfabSRick Macklem 		case NFSATTRBIT_LAYOUTALIGNMENT:
312290d2dfabSRick Macklem 		case NFSATTRBIT_LAYOUTBLKSIZE:
312390d2dfabSRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3124ee29e6f3SRick Macklem 			*tl = txdr_unsigned(nfs_srvmaxio);
312590d2dfabSRick Macklem 			retnum += NFSX_UNSIGNED;
312690d2dfabSRick Macklem 			break;
3127c057a378SRick Macklem 		case NFSATTRBIT_XATTRSUPPORT:
3128c057a378SRick Macklem 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
3129c057a378SRick Macklem 			if (xattrsupp)
3130c057a378SRick Macklem 				*tl = newnfs_true;
3131c057a378SRick Macklem 			else
3132c057a378SRick Macklem 				*tl = newnfs_false;
3133c057a378SRick Macklem 			retnum += NFSX_UNSIGNED;
3134c057a378SRick Macklem 			break;
31352477e88bSRick Macklem 		case NFSATTRBIT_MODEUMASK:
31362477e88bSRick Macklem 			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
31372477e88bSRick Macklem 			/*
31382477e88bSRick Macklem 			 * Since FreeBSD applies the umask above the VFS/VOP,
31392477e88bSRick Macklem 			 * there is no umask to handle here.  If FreeBSD
31402477e88bSRick Macklem 			 * moves handling of umask to below the VFS/VOP,
31412477e88bSRick Macklem 			 * this could change.
31422477e88bSRick Macklem 			 */
31432477e88bSRick Macklem 			*tl++ = vtonfsv34_mode(vap->va_mode);
31442477e88bSRick Macklem 			*tl = 0;
31452477e88bSRick Macklem 			retnum += 2 * NFSX_UNSIGNED;
31462477e88bSRick Macklem 			break;
3147709c1891SRick Macklem 		case NFSATTRBIT_CHANGEATTRTYPE:
3148709c1891SRick Macklem 			NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
3149709c1891SRick Macklem 			*tl = txdr_unsigned(NFSV4CHANGETYPE_UNDEFINED);
3150709c1891SRick Macklem 			if (mp != NULL) {
3151709c1891SRick Macklem 				if ((mp->mnt_vfc->vfc_flags &
3152709c1891SRick Macklem 				    VFCF_FILEREVINC) != 0)
3153709c1891SRick Macklem 					*tl = txdr_unsigned(
3154709c1891SRick Macklem 					   NFSV4CHANGETYPE_VERS_COUNTER_NOPNFS);
3155709c1891SRick Macklem 				else if ((mp->mnt_vfc->vfc_flags &
3156709c1891SRick Macklem 				    VFCF_FILEREVCT) != 0)
3157709c1891SRick Macklem 					*tl = txdr_unsigned(
3158709c1891SRick Macklem 					   NFSV4CHANGETYPE_TIME_METADATA);
3159709c1891SRick Macklem 			}
3160709c1891SRick Macklem 			retnum += NFSX_UNSIGNED;
3161709c1891SRick Macklem 			break;
31629ec7b004SRick Macklem 		default:
31639ec7b004SRick Macklem 			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
316474b8d63dSPedro F. Giffuni 		}
31659ec7b004SRick Macklem 	    }
31669ec7b004SRick Macklem 	}
31679ec7b004SRick Macklem 	if (naclp != NULL)
31689ec7b004SRick Macklem 		acl_free(naclp);
31692f304845SKonstantin Belousov 	free(fs, M_STATFS);
31709ec7b004SRick Macklem 	*retnump = txdr_unsigned(retnum);
31719ec7b004SRick Macklem 	return (retnum + prefixnum);
31729ec7b004SRick Macklem }
31739ec7b004SRick Macklem 
31749ec7b004SRick Macklem /*
31752d90ef47SRick Macklem  * Calculate the files available attribute value.
31762d90ef47SRick Macklem  */
31772d90ef47SRick Macklem static uint32_t
nfsv4_filesavail(struct statfs * fs,struct mount * mp)31782d90ef47SRick Macklem nfsv4_filesavail(struct statfs *fs, struct mount *mp)
31792d90ef47SRick Macklem {
31802d90ef47SRick Macklem 	uint32_t freenum;
31812d90ef47SRick Macklem #ifdef QUOTA
31822d90ef47SRick Macklem 	struct dqblk dqb;
31832d90ef47SRick Macklem 	uid_t savuid;
31842d90ef47SRick Macklem 	NFSPROC_T *p;
31852d90ef47SRick Macklem #endif
31862d90ef47SRick Macklem 
31872d90ef47SRick Macklem 	/*
31882d90ef47SRick Macklem 	 * Check quota and use min(quota, f_ffree).
31892d90ef47SRick Macklem 	 */
31902d90ef47SRick Macklem 	freenum = fs->f_ffree;
31912d90ef47SRick Macklem #ifdef QUOTA
31922d90ef47SRick Macklem 	/*
31932d90ef47SRick Macklem 	 * This is old OpenBSD code that does not build
31942d90ef47SRick Macklem 	 * for FreeBSD.  I do not know if doing this is
31952d90ef47SRick Macklem 	 * useful, so I will just leave the code here.
31962d90ef47SRick Macklem 	 */
31972d90ef47SRick Macklem 	p = curthread();
31982d90ef47SRick Macklem 	/*
31992d90ef47SRick Macklem 	 * ufs_quotactl() insists that the uid argument
32002d90ef47SRick Macklem 	 * equal p_ruid for non-root quota access, so
32012d90ef47SRick Macklem 	 * we'll just make sure that's the case.
32022d90ef47SRick Macklem 	 */
32032d90ef47SRick Macklem 	savuid = p->p_cred->p_ruid;
32042d90ef47SRick Macklem 	p->p_cred->p_ruid = cred->cr_uid;
32052d90ef47SRick Macklem 	if (!VFS_QUOTACTL(mp, QCMD(Q_GETQUOTA,USRQUOTA),
32062d90ef47SRick Macklem 	    cred->cr_uid, &dqb))
32072d90ef47SRick Macklem 	    freenum = min(dqb.dqb_isoftlimit-dqb.dqb_curinodes,
32082d90ef47SRick Macklem 		freenum);
32092d90ef47SRick Macklem 	p->p_cred->p_ruid = savuid;
32102d90ef47SRick Macklem #endif	/* QUOTA */
32112d90ef47SRick Macklem 	return (freenum);
32122d90ef47SRick Macklem }
32132d90ef47SRick Macklem 
32142d90ef47SRick Macklem /*
32159ec7b004SRick Macklem  * Put the attribute bits onto an mbuf list.
32169ec7b004SRick Macklem  * Return the number of bytes of output generated.
32179ec7b004SRick Macklem  */
3218b9cc3262SRyan Moeller int
nfsrv_putattrbit(struct nfsrv_descript * nd,nfsattrbit_t * attrbitp)32199ec7b004SRick Macklem nfsrv_putattrbit(struct nfsrv_descript *nd, nfsattrbit_t *attrbitp)
32209ec7b004SRick Macklem {
32219ec7b004SRick Macklem 	u_int32_t *tl;
32229ec7b004SRick Macklem 	int cnt, i, bytesize;
32239ec7b004SRick Macklem 
32249ec7b004SRick Macklem 	for (cnt = NFSATTRBIT_MAXWORDS; cnt > 0; cnt--)
32259ec7b004SRick Macklem 		if (attrbitp->bits[cnt - 1])
32269ec7b004SRick Macklem 			break;
32279ec7b004SRick Macklem 	bytesize = (cnt + 1) * NFSX_UNSIGNED;
32289ec7b004SRick Macklem 	NFSM_BUILD(tl, u_int32_t *, bytesize);
32299ec7b004SRick Macklem 	*tl++ = txdr_unsigned(cnt);
32309ec7b004SRick Macklem 	for (i = 0; i < cnt; i++)
32319ec7b004SRick Macklem 		*tl++ = txdr_unsigned(attrbitp->bits[i]);
32329ec7b004SRick Macklem 	return (bytesize);
32339ec7b004SRick Macklem }
32349ec7b004SRick Macklem 
32359ec7b004SRick Macklem /*
3236f4179ad4SRick Macklem  * Put the operation bits onto an mbuf list.
3237f4179ad4SRick Macklem  * Return the number of bytes of output generated.
3238f4179ad4SRick Macklem  */
3239f4179ad4SRick Macklem int
nfsrv_putopbit(struct nfsrv_descript * nd,nfsopbit_t * opbitp)3240f4179ad4SRick Macklem nfsrv_putopbit(struct nfsrv_descript *nd, nfsopbit_t *opbitp)
3241f4179ad4SRick Macklem {
3242f4179ad4SRick Macklem 	uint32_t *tl;
3243f4179ad4SRick Macklem 	int cnt, i, bytesize;
3244f4179ad4SRick Macklem 
3245f4179ad4SRick Macklem 	for (cnt = NFSOPBIT_MAXWORDS; cnt > 0; cnt--)
3246f4179ad4SRick Macklem 		if (opbitp->bits[cnt - 1])
3247f4179ad4SRick Macklem 			break;
3248f4179ad4SRick Macklem 	bytesize = (cnt + 1) * NFSX_UNSIGNED;
3249f4179ad4SRick Macklem 	NFSM_BUILD(tl, uint32_t *, bytesize);
3250f4179ad4SRick Macklem 	*tl++ = txdr_unsigned(cnt);
3251f4179ad4SRick Macklem 	for (i = 0; i < cnt; i++)
3252f4179ad4SRick Macklem 		*tl++ = txdr_unsigned(opbitp->bits[i]);
3253f4179ad4SRick Macklem 	return (bytesize);
3254f4179ad4SRick Macklem }
3255f4179ad4SRick Macklem 
3256f4179ad4SRick Macklem /*
32579ec7b004SRick Macklem  * Convert a uid to a string.
32589ec7b004SRick Macklem  * If the lookup fails, just output the digits.
32599ec7b004SRick Macklem  * uid - the user id
32609ec7b004SRick Macklem  * cpp - points to a buffer of size NFSV4_SMALLSTR
32619ec7b004SRick Macklem  *       (malloc a larger one, as required)
32629ec7b004SRick Macklem  * retlenp - pointer to length to be returned
32639ec7b004SRick Macklem  */
3264b9cc3262SRyan Moeller void
nfsv4_uidtostr(uid_t uid,u_char ** cpp,int * retlenp)32650f86b94aSEdward Tomasz Napierala nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp)
32669ec7b004SRick Macklem {
32679ec7b004SRick Macklem 	int i;
32689ec7b004SRick Macklem 	struct nfsusrgrp *usrp;
32699ec7b004SRick Macklem 	u_char *cp = *cpp;
32709ec7b004SRick Macklem 	uid_t tmp;
32719ec7b004SRick Macklem 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
327284be7e09SRick Macklem 	struct nfsrv_lughash *hp;
32739ec7b004SRick Macklem 
3274f0db2b60SRick Macklem 	NFSD_CURVNET_SET_QUIET(NFSD_TD_TO_VNET(curthread));
32759ec7b004SRick Macklem 	cnt = 0;
32769ec7b004SRick Macklem tryagain:
3277f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_dnsnamelen) > 0 &&
3278f0db2b60SRick Macklem 	    !NFSD_VNET(nfs_enable_uidtostring)) {
32799ec7b004SRick Macklem 		/*
32809ec7b004SRick Macklem 		 * Always map nfsrv_defaultuid to "nobody".
32819ec7b004SRick Macklem 		 */
3282f0db2b60SRick Macklem 		if (uid == NFSD_VNET(nfsrv_defaultuid)) {
3283f0db2b60SRick Macklem 			i = NFSD_VNET(nfsrv_dnsnamelen) + 7;
32849ec7b004SRick Macklem 			if (i > len) {
32859ec7b004SRick Macklem 				if (len > NFSV4_SMALLSTR)
32869ec7b004SRick Macklem 					free(cp, M_NFSSTRING);
32879ec7b004SRick Macklem 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
32889ec7b004SRick Macklem 				*cpp = cp;
32899ec7b004SRick Macklem 				len = i;
32909ec7b004SRick Macklem 				goto tryagain;
32919ec7b004SRick Macklem 			}
32929ec7b004SRick Macklem 			*retlenp = i;
32939ec7b004SRick Macklem 			NFSBCOPY("nobody@", cp, 7);
32949ec7b004SRick Macklem 			cp += 7;
3295f0db2b60SRick Macklem 			NFSBCOPY(NFSD_VNET(nfsrv_dnsname), cp,
3296f0db2b60SRick Macklem 			    NFSD_VNET(nfsrv_dnsnamelen));
3297f0db2b60SRick Macklem 			NFSD_CURVNET_RESTORE();
32989ec7b004SRick Macklem 			return;
32999ec7b004SRick Macklem 		}
33009ec7b004SRick Macklem 		hasampersand = 0;
330184be7e09SRick Macklem 		hp = NFSUSERHASH(uid);
330284be7e09SRick Macklem 		mtx_lock(&hp->mtx);
330384be7e09SRick Macklem 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
33049ec7b004SRick Macklem 			if (usrp->lug_uid == uid) {
33059ec7b004SRick Macklem 				if (usrp->lug_expiry < NFSD_MONOSEC)
33069ec7b004SRick Macklem 					break;
33079ec7b004SRick Macklem 				/*
33089ec7b004SRick Macklem 				 * If the name doesn't already have an '@'
33099ec7b004SRick Macklem 				 * in it, append @domainname to it.
33109ec7b004SRick Macklem 				 */
33119ec7b004SRick Macklem 				for (i = 0; i < usrp->lug_namelen; i++) {
33129ec7b004SRick Macklem 					if (usrp->lug_name[i] == '@') {
33139ec7b004SRick Macklem 						hasampersand = 1;
33149ec7b004SRick Macklem 						break;
33159ec7b004SRick Macklem 					}
33169ec7b004SRick Macklem 				}
33179ec7b004SRick Macklem 				if (hasampersand)
33189ec7b004SRick Macklem 					i = usrp->lug_namelen;
33199ec7b004SRick Macklem 				else
33209ec7b004SRick Macklem 					i = usrp->lug_namelen +
3321f0db2b60SRick Macklem 					    NFSD_VNET(nfsrv_dnsnamelen) + 1;
33229ec7b004SRick Macklem 				if (i > len) {
332384be7e09SRick Macklem 					mtx_unlock(&hp->mtx);
33249ec7b004SRick Macklem 					if (len > NFSV4_SMALLSTR)
33259ec7b004SRick Macklem 						free(cp, M_NFSSTRING);
33269ec7b004SRick Macklem 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
33279ec7b004SRick Macklem 					*cpp = cp;
33289ec7b004SRick Macklem 					len = i;
33299ec7b004SRick Macklem 					goto tryagain;
33309ec7b004SRick Macklem 				}
33319ec7b004SRick Macklem 				*retlenp = i;
33329ec7b004SRick Macklem 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
33339ec7b004SRick Macklem 				if (!hasampersand) {
33349ec7b004SRick Macklem 					cp += usrp->lug_namelen;
33359ec7b004SRick Macklem 					*cp++ = '@';
3336f0db2b60SRick Macklem 					NFSBCOPY(NFSD_VNET(nfsrv_dnsname), cp,
3337f0db2b60SRick Macklem 					    NFSD_VNET(nfsrv_dnsnamelen));
33389ec7b004SRick Macklem 				}
333984be7e09SRick Macklem 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
334084be7e09SRick Macklem 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
334184be7e09SRick Macklem 				    lug_numhash);
334284be7e09SRick Macklem 				mtx_unlock(&hp->mtx);
3343f0db2b60SRick Macklem 				NFSD_CURVNET_RESTORE();
33449ec7b004SRick Macklem 				return;
33459ec7b004SRick Macklem 			}
33469ec7b004SRick Macklem 		}
334784be7e09SRick Macklem 		mtx_unlock(&hp->mtx);
33489ec7b004SRick Macklem 		cnt++;
3349f32bf292SEdward Tomasz Napierala 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0, NULL);
33509ec7b004SRick Macklem 		if (ret == 0 && cnt < 2)
33519ec7b004SRick Macklem 			goto tryagain;
33529ec7b004SRick Macklem 	}
33539ec7b004SRick Macklem 
33549ec7b004SRick Macklem 	/*
33559ec7b004SRick Macklem 	 * No match, just return a string of digits.
33569ec7b004SRick Macklem 	 */
33579ec7b004SRick Macklem 	tmp = uid;
33589ec7b004SRick Macklem 	i = 0;
33599ec7b004SRick Macklem 	while (tmp || i == 0) {
33609ec7b004SRick Macklem 		tmp /= 10;
33619ec7b004SRick Macklem 		i++;
33629ec7b004SRick Macklem 	}
33639ec7b004SRick Macklem 	len = (i > len) ? len : i;
33649ec7b004SRick Macklem 	*retlenp = len;
33659ec7b004SRick Macklem 	cp += (len - 1);
33669ec7b004SRick Macklem 	tmp = uid;
33679ec7b004SRick Macklem 	for (i = 0; i < len; i++) {
33689ec7b004SRick Macklem 		*cp-- = '0' + (tmp % 10);
33699ec7b004SRick Macklem 		tmp /= 10;
33709ec7b004SRick Macklem 	}
3371f0db2b60SRick Macklem 	NFSD_CURVNET_RESTORE();
33729ec7b004SRick Macklem 	return;
33739ec7b004SRick Macklem }
33749ec7b004SRick Macklem 
33759ec7b004SRick Macklem /*
337684be7e09SRick Macklem  * Get a credential for the uid with the server's group list.
337784be7e09SRick Macklem  * If none is found, just return the credential passed in after
337884be7e09SRick Macklem  * logging a warning message.
337984be7e09SRick Macklem  */
338084be7e09SRick Macklem struct ucred *
nfsrv_getgrpscred(struct ucred * oldcred)338184be7e09SRick Macklem nfsrv_getgrpscred(struct ucred *oldcred)
338284be7e09SRick Macklem {
338384be7e09SRick Macklem 	struct nfsusrgrp *usrp;
338484be7e09SRick Macklem 	struct ucred *newcred;
338584be7e09SRick Macklem 	int cnt, ret;
338684be7e09SRick Macklem 	uid_t uid;
338784be7e09SRick Macklem 	struct nfsrv_lughash *hp;
338884be7e09SRick Macklem 
338984be7e09SRick Macklem 	cnt = 0;
339084be7e09SRick Macklem 	uid = oldcred->cr_uid;
339184be7e09SRick Macklem tryagain:
3392f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_dnsnamelen) > 0) {
339384be7e09SRick Macklem 		hp = NFSUSERHASH(uid);
339484be7e09SRick Macklem 		mtx_lock(&hp->mtx);
339584be7e09SRick Macklem 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
339684be7e09SRick Macklem 			if (usrp->lug_uid == uid) {
339784be7e09SRick Macklem 				if (usrp->lug_expiry < NFSD_MONOSEC)
339884be7e09SRick Macklem 					break;
339984be7e09SRick Macklem 				if (usrp->lug_cred != NULL) {
340084be7e09SRick Macklem 					newcred = crhold(usrp->lug_cred);
340184be7e09SRick Macklem 					crfree(oldcred);
340284be7e09SRick Macklem 				} else
340384be7e09SRick Macklem 					newcred = oldcred;
340484be7e09SRick Macklem 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
340584be7e09SRick Macklem 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
340684be7e09SRick Macklem 				    lug_numhash);
340784be7e09SRick Macklem 				mtx_unlock(&hp->mtx);
340884be7e09SRick Macklem 				return (newcred);
340984be7e09SRick Macklem 			}
341084be7e09SRick Macklem 		}
341184be7e09SRick Macklem 		mtx_unlock(&hp->mtx);
341284be7e09SRick Macklem 		cnt++;
3413f32bf292SEdward Tomasz Napierala 		ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0, NULL);
341484be7e09SRick Macklem 		if (ret == 0 && cnt < 2)
341584be7e09SRick Macklem 			goto tryagain;
341684be7e09SRick Macklem 	}
341784be7e09SRick Macklem 	return (oldcred);
341884be7e09SRick Macklem }
341984be7e09SRick Macklem 
342084be7e09SRick Macklem /*
34219ec7b004SRick Macklem  * Convert a string to a uid.
34229ec7b004SRick Macklem  * If no conversion is possible return NFSERR_BADOWNER, otherwise
34239ec7b004SRick Macklem  * return 0.
3424c52005a3SRick Macklem  * If this is called from a client side mount using AUTH_SYS and the
3425c52005a3SRick Macklem  * string is made up entirely of digits, just convert the string to
3426c52005a3SRick Macklem  * a number.
34279ec7b004SRick Macklem  */
3428b9cc3262SRyan Moeller int
nfsv4_strtouid(struct nfsrv_descript * nd,u_char * str,int len,uid_t * uidp)34290658ac39SEdward Tomasz Napierala nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp)
34309ec7b004SRick Macklem {
34319ec7b004SRick Macklem 	int i;
3432c52005a3SRick Macklem 	char *cp, *endstr, *str0;
34339ec7b004SRick Macklem 	struct nfsusrgrp *usrp;
34349ec7b004SRick Macklem 	int cnt, ret;
3435a9285ae5SZack Kirsch 	int error = 0;
3436c52005a3SRick Macklem 	uid_t tuid;
343784be7e09SRick Macklem 	struct nfsrv_lughash *hp, *hp2;
34389ec7b004SRick Macklem 
3439f0db2b60SRick Macklem 	NFSD_CURVNET_SET_QUIET(NFSD_TD_TO_VNET(curthread));
3440a9285ae5SZack Kirsch 	if (len == 0) {
3441a9285ae5SZack Kirsch 		error = NFSERR_BADOWNER;
3442a9285ae5SZack Kirsch 		goto out;
3443a9285ae5SZack Kirsch 	}
3444c52005a3SRick Macklem 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
3445c52005a3SRick Macklem 	str0 = str;
3446c52005a3SRick Macklem 	tuid = (uid_t)strtoul(str0, &endstr, 10);
3447ca20bd92SRick Macklem 	if ((endstr - str0) == len) {
3448ca20bd92SRick Macklem 		/* A numeric string. */
3449ca20bd92SRick Macklem 		if ((nd->nd_flag & ND_KERBV) == 0 &&
3450ca20bd92SRick Macklem 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
3451f0db2b60SRick Macklem 		      NFSD_VNET(nfsd_enable_stringtouid) != 0))
3452c52005a3SRick Macklem 			*uidp = tuid;
3453ca20bd92SRick Macklem 		else
3454ca20bd92SRick Macklem 			error = NFSERR_BADOWNER;
3455c52005a3SRick Macklem 		goto out;
3456c52005a3SRick Macklem 	}
34579ec7b004SRick Macklem 	/*
34589ec7b004SRick Macklem 	 * Look for an '@'.
34599ec7b004SRick Macklem 	 */
3460c52005a3SRick Macklem 	cp = strchr(str0, '@');
3461c52005a3SRick Macklem 	if (cp != NULL)
3462c52005a3SRick Macklem 		i = (int)(cp++ - str0);
3463c52005a3SRick Macklem 	else
3464c52005a3SRick Macklem 		i = len;
34659ec7b004SRick Macklem 
34669ec7b004SRick Macklem 	cnt = 0;
34679ec7b004SRick Macklem tryagain:
3468f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_dnsnamelen) > 0) {
34699ec7b004SRick Macklem 		/*
347084be7e09SRick Macklem 		 * If an '@' is found and the domain name matches, search for
347184be7e09SRick Macklem 		 * the name with dns stripped off.
34720347ddf4SRick Macklem 		 * The match for alphabetics in now case insensitive,
34730347ddf4SRick Macklem 		 * since RFC8881 defines this string as a DNS domain name.
34749ec7b004SRick Macklem 		 */
347584be7e09SRick Macklem 		if (cnt == 0 && i < len && i > 0 &&
3476f0db2b60SRick Macklem 		    (len - 1 - i) == NFSD_VNET(nfsrv_dnsnamelen) &&
34770347ddf4SRick Macklem 		    strncasecmp(cp, NFSD_VNET(nfsrv_dnsname),
34780347ddf4SRick Macklem 		     NFSD_VNET(nfsrv_dnsnamelen)) == 0) {
3479f0db2b60SRick Macklem 			len -= (NFSD_VNET(nfsrv_dnsnamelen) + 1);
34809ec7b004SRick Macklem 			*(cp - 1) = '\0';
34819ec7b004SRick Macklem 		}
34829ec7b004SRick Macklem 
34839ec7b004SRick Macklem 		/*
34849ec7b004SRick Macklem 		 * Check for the special case of "nobody".
34859ec7b004SRick Macklem 		 */
34869ec7b004SRick Macklem 		if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
3487f0db2b60SRick Macklem 			*uidp = NFSD_VNET(nfsrv_defaultuid);
3488a9285ae5SZack Kirsch 			error = 0;
3489a9285ae5SZack Kirsch 			goto out;
34909ec7b004SRick Macklem 		}
34919ec7b004SRick Macklem 
349284be7e09SRick Macklem 		hp = NFSUSERNAMEHASH(str, len);
349384be7e09SRick Macklem 		mtx_lock(&hp->mtx);
349484be7e09SRick Macklem 		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
34959ec7b004SRick Macklem 			if (usrp->lug_namelen == len &&
34969ec7b004SRick Macklem 			    !NFSBCMP(usrp->lug_name, str, len)) {
34979ec7b004SRick Macklem 				if (usrp->lug_expiry < NFSD_MONOSEC)
34989ec7b004SRick Macklem 					break;
349984be7e09SRick Macklem 				hp2 = NFSUSERHASH(usrp->lug_uid);
350084be7e09SRick Macklem 				mtx_lock(&hp2->mtx);
350184be7e09SRick Macklem 				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
350284be7e09SRick Macklem 				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
350384be7e09SRick Macklem 				    lug_numhash);
35049ec7b004SRick Macklem 				*uidp = usrp->lug_uid;
350584be7e09SRick Macklem 				mtx_unlock(&hp2->mtx);
350684be7e09SRick Macklem 				mtx_unlock(&hp->mtx);
3507a9285ae5SZack Kirsch 				error = 0;
3508a9285ae5SZack Kirsch 				goto out;
35099ec7b004SRick Macklem 			}
35109ec7b004SRick Macklem 		}
351184be7e09SRick Macklem 		mtx_unlock(&hp->mtx);
35129ec7b004SRick Macklem 		cnt++;
35139ec7b004SRick Macklem 		ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
3514f32bf292SEdward Tomasz Napierala 		    str);
35159ec7b004SRick Macklem 		if (ret == 0 && cnt < 2)
35169ec7b004SRick Macklem 			goto tryagain;
351784be7e09SRick Macklem 	}
3518a9285ae5SZack Kirsch 	error = NFSERR_BADOWNER;
3519a9285ae5SZack Kirsch 
3520a9285ae5SZack Kirsch out:
3521f0db2b60SRick Macklem 	NFSD_CURVNET_RESTORE();
3522a9285ae5SZack Kirsch 	NFSEXITCODE(error);
3523a9285ae5SZack Kirsch 	return (error);
35249ec7b004SRick Macklem }
35259ec7b004SRick Macklem 
35269ec7b004SRick Macklem /*
35279ec7b004SRick Macklem  * Convert a gid to a string.
35289ec7b004SRick Macklem  * gid - the group id
35299ec7b004SRick Macklem  * cpp - points to a buffer of size NFSV4_SMALLSTR
35309ec7b004SRick Macklem  *       (malloc a larger one, as required)
35319ec7b004SRick Macklem  * retlenp - pointer to length to be returned
35329ec7b004SRick Macklem  */
3533b9cc3262SRyan Moeller void
nfsv4_gidtostr(gid_t gid,u_char ** cpp,int * retlenp)3534c703cba8SEdward Tomasz Napierala nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp)
35359ec7b004SRick Macklem {
35369ec7b004SRick Macklem 	int i;
35379ec7b004SRick Macklem 	struct nfsusrgrp *usrp;
35389ec7b004SRick Macklem 	u_char *cp = *cpp;
35399ec7b004SRick Macklem 	gid_t tmp;
35409ec7b004SRick Macklem 	int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
354184be7e09SRick Macklem 	struct nfsrv_lughash *hp;
35429ec7b004SRick Macklem 
3543f0db2b60SRick Macklem 	NFSD_CURVNET_SET_QUIET(NFSD_TD_TO_VNET(curthread));
35449ec7b004SRick Macklem 	cnt = 0;
35459ec7b004SRick Macklem tryagain:
3546f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_dnsnamelen) > 0 &&
3547f0db2b60SRick Macklem 	    !NFSD_VNET(nfs_enable_uidtostring)) {
35489ec7b004SRick Macklem 		/*
35499ec7b004SRick Macklem 		 * Always map nfsrv_defaultgid to "nogroup".
35509ec7b004SRick Macklem 		 */
3551f0db2b60SRick Macklem 		if (gid == NFSD_VNET(nfsrv_defaultgid)) {
3552f0db2b60SRick Macklem 			i = NFSD_VNET(nfsrv_dnsnamelen) + 8;
35539ec7b004SRick Macklem 			if (i > len) {
35549ec7b004SRick Macklem 				if (len > NFSV4_SMALLSTR)
35559ec7b004SRick Macklem 					free(cp, M_NFSSTRING);
35569ec7b004SRick Macklem 				cp = malloc(i, M_NFSSTRING, M_WAITOK);
35579ec7b004SRick Macklem 				*cpp = cp;
35589ec7b004SRick Macklem 				len = i;
35599ec7b004SRick Macklem 				goto tryagain;
35609ec7b004SRick Macklem 			}
35619ec7b004SRick Macklem 			*retlenp = i;
35629ec7b004SRick Macklem 			NFSBCOPY("nogroup@", cp, 8);
35639ec7b004SRick Macklem 			cp += 8;
3564f0db2b60SRick Macklem 			NFSBCOPY(NFSD_VNET(nfsrv_dnsname), cp,
3565f0db2b60SRick Macklem 			    NFSD_VNET(nfsrv_dnsnamelen));
3566f0db2b60SRick Macklem 			NFSD_CURVNET_RESTORE();
35679ec7b004SRick Macklem 			return;
35689ec7b004SRick Macklem 		}
35699ec7b004SRick Macklem 		hasampersand = 0;
357084be7e09SRick Macklem 		hp = NFSGROUPHASH(gid);
357184be7e09SRick Macklem 		mtx_lock(&hp->mtx);
357284be7e09SRick Macklem 		TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
35739ec7b004SRick Macklem 			if (usrp->lug_gid == gid) {
35749ec7b004SRick Macklem 				if (usrp->lug_expiry < NFSD_MONOSEC)
35759ec7b004SRick Macklem 					break;
35769ec7b004SRick Macklem 				/*
35779ec7b004SRick Macklem 				 * If the name doesn't already have an '@'
35789ec7b004SRick Macklem 				 * in it, append @domainname to it.
35799ec7b004SRick Macklem 				 */
35809ec7b004SRick Macklem 				for (i = 0; i < usrp->lug_namelen; i++) {
35819ec7b004SRick Macklem 					if (usrp->lug_name[i] == '@') {
35829ec7b004SRick Macklem 						hasampersand = 1;
35839ec7b004SRick Macklem 						break;
35849ec7b004SRick Macklem 					}
35859ec7b004SRick Macklem 				}
35869ec7b004SRick Macklem 				if (hasampersand)
35879ec7b004SRick Macklem 					i = usrp->lug_namelen;
35889ec7b004SRick Macklem 				else
35899ec7b004SRick Macklem 					i = usrp->lug_namelen +
3590f0db2b60SRick Macklem 					    NFSD_VNET(nfsrv_dnsnamelen) + 1;
35919ec7b004SRick Macklem 				if (i > len) {
359284be7e09SRick Macklem 					mtx_unlock(&hp->mtx);
35939ec7b004SRick Macklem 					if (len > NFSV4_SMALLSTR)
35949ec7b004SRick Macklem 						free(cp, M_NFSSTRING);
35959ec7b004SRick Macklem 					cp = malloc(i, M_NFSSTRING, M_WAITOK);
35969ec7b004SRick Macklem 					*cpp = cp;
35979ec7b004SRick Macklem 					len = i;
35989ec7b004SRick Macklem 					goto tryagain;
35999ec7b004SRick Macklem 				}
36009ec7b004SRick Macklem 				*retlenp = i;
36019ec7b004SRick Macklem 				NFSBCOPY(usrp->lug_name, cp, usrp->lug_namelen);
36029ec7b004SRick Macklem 				if (!hasampersand) {
36039ec7b004SRick Macklem 					cp += usrp->lug_namelen;
36049ec7b004SRick Macklem 					*cp++ = '@';
3605f0db2b60SRick Macklem 					NFSBCOPY(NFSD_VNET(nfsrv_dnsname), cp,
3606f0db2b60SRick Macklem 					    NFSD_VNET(nfsrv_dnsnamelen));
36079ec7b004SRick Macklem 				}
360884be7e09SRick Macklem 				TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
360984be7e09SRick Macklem 				TAILQ_INSERT_TAIL(&hp->lughead, usrp,
361084be7e09SRick Macklem 				    lug_numhash);
361184be7e09SRick Macklem 				mtx_unlock(&hp->mtx);
3612f0db2b60SRick Macklem 				NFSD_CURVNET_RESTORE();
36139ec7b004SRick Macklem 				return;
36149ec7b004SRick Macklem 			}
36159ec7b004SRick Macklem 		}
361684be7e09SRick Macklem 		mtx_unlock(&hp->mtx);
36179ec7b004SRick Macklem 		cnt++;
3618f32bf292SEdward Tomasz Napierala 		ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid, NULL);
36199ec7b004SRick Macklem 		if (ret == 0 && cnt < 2)
36209ec7b004SRick Macklem 			goto tryagain;
36219ec7b004SRick Macklem 	}
36229ec7b004SRick Macklem 
36239ec7b004SRick Macklem 	/*
36249ec7b004SRick Macklem 	 * No match, just return a string of digits.
36259ec7b004SRick Macklem 	 */
36269ec7b004SRick Macklem 	tmp = gid;
36279ec7b004SRick Macklem 	i = 0;
36289ec7b004SRick Macklem 	while (tmp || i == 0) {
36299ec7b004SRick Macklem 		tmp /= 10;
36309ec7b004SRick Macklem 		i++;
36319ec7b004SRick Macklem 	}
36329ec7b004SRick Macklem 	len = (i > len) ? len : i;
36339ec7b004SRick Macklem 	*retlenp = len;
36349ec7b004SRick Macklem 	cp += (len - 1);
36359ec7b004SRick Macklem 	tmp = gid;
36369ec7b004SRick Macklem 	for (i = 0; i < len; i++) {
36379ec7b004SRick Macklem 		*cp-- = '0' + (tmp % 10);
36389ec7b004SRick Macklem 		tmp /= 10;
36399ec7b004SRick Macklem 	}
3640f0db2b60SRick Macklem 	NFSD_CURVNET_RESTORE();
36419ec7b004SRick Macklem 	return;
36429ec7b004SRick Macklem }
36439ec7b004SRick Macklem 
36449ec7b004SRick Macklem /*
36459ec7b004SRick Macklem  * Convert a string to a gid.
3646c52005a3SRick Macklem  * If no conversion is possible return NFSERR_BADOWNER, otherwise
3647c52005a3SRick Macklem  * return 0.
3648c52005a3SRick Macklem  * If this is called from a client side mount using AUTH_SYS and the
3649c52005a3SRick Macklem  * string is made up entirely of digits, just convert the string to
3650c52005a3SRick Macklem  * a number.
36519ec7b004SRick Macklem  */
3652b9cc3262SRyan Moeller int
nfsv4_strtogid(struct nfsrv_descript * nd,u_char * str,int len,gid_t * gidp)36532df8bd90SEdward Tomasz Napierala nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp)
36549ec7b004SRick Macklem {
36559ec7b004SRick Macklem 	int i;
3656c52005a3SRick Macklem 	char *cp, *endstr, *str0;
36579ec7b004SRick Macklem 	struct nfsusrgrp *usrp;
36589ec7b004SRick Macklem 	int cnt, ret;
3659a9285ae5SZack Kirsch 	int error = 0;
3660c52005a3SRick Macklem 	gid_t tgid;
366184be7e09SRick Macklem 	struct nfsrv_lughash *hp, *hp2;
36629ec7b004SRick Macklem 
3663f0db2b60SRick Macklem 	NFSD_CURVNET_SET_QUIET(NFSD_TD_TO_VNET(curthread));
3664a9285ae5SZack Kirsch 	if (len == 0) {
3665a9285ae5SZack Kirsch 		error =  NFSERR_BADOWNER;
3666a9285ae5SZack Kirsch 		goto out;
3667a9285ae5SZack Kirsch 	}
3668c52005a3SRick Macklem 	/* If a string of digits and an AUTH_SYS mount, just convert it. */
3669c52005a3SRick Macklem 	str0 = str;
3670c52005a3SRick Macklem 	tgid = (gid_t)strtoul(str0, &endstr, 10);
3671ca20bd92SRick Macklem 	if ((endstr - str0) == len) {
3672ca20bd92SRick Macklem 		/* A numeric string. */
3673ca20bd92SRick Macklem 		if ((nd->nd_flag & ND_KERBV) == 0 &&
3674ca20bd92SRick Macklem 		    ((nd->nd_flag & ND_NFSCL) != 0 ||
3675f0db2b60SRick Macklem 		      NFSD_VNET(nfsd_enable_stringtouid) != 0))
3676c52005a3SRick Macklem 			*gidp = tgid;
3677ca20bd92SRick Macklem 		else
3678ca20bd92SRick Macklem 			error = NFSERR_BADOWNER;
3679c52005a3SRick Macklem 		goto out;
3680c52005a3SRick Macklem 	}
36819ec7b004SRick Macklem 	/*
36829ec7b004SRick Macklem 	 * Look for an '@'.
36839ec7b004SRick Macklem 	 */
3684c52005a3SRick Macklem 	cp = strchr(str0, '@');
3685c52005a3SRick Macklem 	if (cp != NULL)
3686c52005a3SRick Macklem 		i = (int)(cp++ - str0);
3687c52005a3SRick Macklem 	else
3688c52005a3SRick Macklem 		i = len;
36899ec7b004SRick Macklem 
36909ec7b004SRick Macklem 	cnt = 0;
36919ec7b004SRick Macklem tryagain:
3692f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_dnsnamelen) > 0) {
36939ec7b004SRick Macklem 		/*
369484be7e09SRick Macklem 		 * If an '@' is found and the dns name matches, search for the
369584be7e09SRick Macklem 		 * name with the dns stripped off.
36969ec7b004SRick Macklem 		 */
369784be7e09SRick Macklem 		if (cnt == 0 && i < len && i > 0 &&
3698f0db2b60SRick Macklem 		    (len - 1 - i) == NFSD_VNET(nfsrv_dnsnamelen) &&
36990347ddf4SRick Macklem 		    strncasecmp(cp, NFSD_VNET(nfsrv_dnsname),
37000347ddf4SRick Macklem 		    NFSD_VNET(nfsrv_dnsnamelen)) == 0) {
3701f0db2b60SRick Macklem 			len -= (NFSD_VNET(nfsrv_dnsnamelen) + 1);
37029ec7b004SRick Macklem 			*(cp - 1) = '\0';
37039ec7b004SRick Macklem 		}
37049ec7b004SRick Macklem 
37059ec7b004SRick Macklem 		/*
37069ec7b004SRick Macklem 		 * Check for the special case of "nogroup".
37079ec7b004SRick Macklem 		 */
37089ec7b004SRick Macklem 		if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
3709f0db2b60SRick Macklem 			*gidp = NFSD_VNET(nfsrv_defaultgid);
3710a9285ae5SZack Kirsch 			error = 0;
3711a9285ae5SZack Kirsch 			goto out;
37129ec7b004SRick Macklem 		}
37139ec7b004SRick Macklem 
371484be7e09SRick Macklem 		hp = NFSGROUPNAMEHASH(str, len);
371584be7e09SRick Macklem 		mtx_lock(&hp->mtx);
371684be7e09SRick Macklem 		TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
37179ec7b004SRick Macklem 			if (usrp->lug_namelen == len &&
37189ec7b004SRick Macklem 			    !NFSBCMP(usrp->lug_name, str, len)) {
37199ec7b004SRick Macklem 				if (usrp->lug_expiry < NFSD_MONOSEC)
37209ec7b004SRick Macklem 					break;
372184be7e09SRick Macklem 				hp2 = NFSGROUPHASH(usrp->lug_gid);
372284be7e09SRick Macklem 				mtx_lock(&hp2->mtx);
372384be7e09SRick Macklem 				TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
372484be7e09SRick Macklem 				TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
372584be7e09SRick Macklem 				    lug_numhash);
37269ec7b004SRick Macklem 				*gidp = usrp->lug_gid;
372784be7e09SRick Macklem 				mtx_unlock(&hp2->mtx);
372884be7e09SRick Macklem 				mtx_unlock(&hp->mtx);
3729a9285ae5SZack Kirsch 				error = 0;
3730a9285ae5SZack Kirsch 				goto out;
37319ec7b004SRick Macklem 			}
37329ec7b004SRick Macklem 		}
373384be7e09SRick Macklem 		mtx_unlock(&hp->mtx);
37349ec7b004SRick Macklem 		cnt++;
37359ec7b004SRick Macklem 		ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
3736f32bf292SEdward Tomasz Napierala 		    str);
37379ec7b004SRick Macklem 		if (ret == 0 && cnt < 2)
37389ec7b004SRick Macklem 			goto tryagain;
373984be7e09SRick Macklem 	}
3740a9285ae5SZack Kirsch 	error = NFSERR_BADOWNER;
3741a9285ae5SZack Kirsch 
3742a9285ae5SZack Kirsch out:
3743f0db2b60SRick Macklem 	NFSD_CURVNET_RESTORE();
3744a9285ae5SZack Kirsch 	NFSEXITCODE(error);
3745a9285ae5SZack Kirsch 	return (error);
37469ec7b004SRick Macklem }
37479ec7b004SRick Macklem 
37489ec7b004SRick Macklem /*
37499ec7b004SRick Macklem  * Set the port for the nfsuserd.
37509ec7b004SRick Macklem  */
3751b9cc3262SRyan Moeller int
nfsrv_nfsuserdport(struct nfsuserd_args * nargs,NFSPROC_T * p)375280405bcfSRick Macklem nfsrv_nfsuserdport(struct nfsuserd_args *nargs, NFSPROC_T *p)
37539ec7b004SRick Macklem {
37549ec7b004SRick Macklem 	struct nfssockreq *rp;
375580405bcfSRick Macklem #ifdef INET
37569ec7b004SRick Macklem 	struct sockaddr_in *ad;
375780405bcfSRick Macklem #endif
375880405bcfSRick Macklem #ifdef INET6
375980405bcfSRick Macklem 	struct sockaddr_in6 *ad6;
376080405bcfSRick Macklem 	const struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT;
376180405bcfSRick Macklem #endif
37629ec7b004SRick Macklem 	int error;
37639ec7b004SRick Macklem 
37649ec7b004SRick Macklem 	NFSLOCKNAMEID();
3765f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_nfsuserd) != NOTRUNNING) {
37669ec7b004SRick Macklem 		NFSUNLOCKNAMEID();
3767a9285ae5SZack Kirsch 		error = EPERM;
3768a9285ae5SZack Kirsch 		goto out;
37699ec7b004SRick Macklem 	}
3770f0db2b60SRick Macklem 	NFSD_VNET(nfsrv_nfsuserd) = STARTSTOP;
37719ec7b004SRick Macklem 	/*
37729ec7b004SRick Macklem 	 * Set up the socket record and connect.
3773e1cda5eeSRick Macklem 	 * Set nr_client NULL before unlocking, just to ensure that no other
3774e1cda5eeSRick Macklem 	 * process/thread/core will use a bogus old value.  This could only
3775e1cda5eeSRick Macklem 	 * occur if the use of the nameid lock to protect nfsrv_nfsuserd is
3776e1cda5eeSRick Macklem 	 * broken.
37779ec7b004SRick Macklem 	 */
3778f0db2b60SRick Macklem 	rp = &NFSD_VNET(nfsrv_nfsuserdsock);
37799ec7b004SRick Macklem 	rp->nr_client = NULL;
3780e1cda5eeSRick Macklem 	NFSUNLOCKNAMEID();
37819ec7b004SRick Macklem 	rp->nr_sotype = SOCK_DGRAM;
37829ec7b004SRick Macklem 	rp->nr_soproto = IPPROTO_UDP;
378302c8dd7dSRick Macklem 	rp->nr_lock = (NFSR_RESERVEDPORT | NFSR_LOCALHOST);
378402c8dd7dSRick Macklem 	rp->nr_cred = NULL;
37859ec7b004SRick Macklem 	rp->nr_prog = RPCPROG_NFSUSERD;
378680405bcfSRick Macklem 	error = 0;
378780405bcfSRick Macklem 	switch (nargs->nuserd_family) {
378880405bcfSRick Macklem #ifdef INET
378980405bcfSRick Macklem 	case AF_INET:
379080405bcfSRick Macklem 		rp->nr_nam = malloc(sizeof(struct sockaddr_in), M_SONAME,
379180405bcfSRick Macklem 		    M_WAITOK | M_ZERO);
379280405bcfSRick Macklem  		ad = (struct sockaddr_in *)rp->nr_nam;
379380405bcfSRick Macklem 		ad->sin_len = sizeof(struct sockaddr_in);
379480405bcfSRick Macklem  		ad->sin_family = AF_INET;
379580405bcfSRick Macklem 		ad->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
379680405bcfSRick Macklem 		ad->sin_port = nargs->nuserd_port;
379780405bcfSRick Macklem 		break;
379880405bcfSRick Macklem #endif
379980405bcfSRick Macklem #ifdef INET6
380080405bcfSRick Macklem 	case AF_INET6:
380180405bcfSRick Macklem 		rp->nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
380280405bcfSRick Macklem 		    M_WAITOK | M_ZERO);
380380405bcfSRick Macklem 		ad6 = (struct sockaddr_in6 *)rp->nr_nam;
380480405bcfSRick Macklem 		ad6->sin6_len = sizeof(struct sockaddr_in6);
380580405bcfSRick Macklem 		ad6->sin6_family = AF_INET6;
380680405bcfSRick Macklem 		ad6->sin6_addr = in6loopback;
380780405bcfSRick Macklem 		ad6->sin6_port = nargs->nuserd_port;
380880405bcfSRick Macklem 		break;
380980405bcfSRick Macklem #endif
381080405bcfSRick Macklem 	default:
381180405bcfSRick Macklem 		error = ENXIO;
381280405bcfSRick Macklem  	}
38139ec7b004SRick Macklem 	rp->nr_vers = RPCNFSUSERD_VERS;
381480405bcfSRick Macklem 	if (error == 0)
38151e0a518dSRick Macklem 		error = newnfs_connect(NULL, rp, NFSPROCCRED(p), p, 0, false,
38161e0a518dSRick Macklem 		    &rp->nr_client);
3817e1cda5eeSRick Macklem 	if (error == 0) {
3818e1cda5eeSRick Macklem 		NFSLOCKNAMEID();
3819f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_nfsuserd) = RUNNING;
3820e1cda5eeSRick Macklem 		NFSUNLOCKNAMEID();
3821e1cda5eeSRick Macklem 	} else {
3822b97b91b5SConrad Meyer 		free(rp->nr_nam, M_SONAME);
3823e1cda5eeSRick Macklem 		NFSLOCKNAMEID();
3824f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_nfsuserd) = NOTRUNNING;
3825e1cda5eeSRick Macklem 		NFSUNLOCKNAMEID();
38269ec7b004SRick Macklem 	}
3827a9285ae5SZack Kirsch out:
3828a9285ae5SZack Kirsch 	NFSEXITCODE(error);
38299ec7b004SRick Macklem 	return (error);
38309ec7b004SRick Macklem }
38319ec7b004SRick Macklem 
38329ec7b004SRick Macklem /*
38339ec7b004SRick Macklem  * Delete the nfsuserd port.
38349ec7b004SRick Macklem  */
3835b9cc3262SRyan Moeller void
nfsrv_nfsuserddelport(void)38369ec7b004SRick Macklem nfsrv_nfsuserddelport(void)
38379ec7b004SRick Macklem {
38389ec7b004SRick Macklem 
38399ec7b004SRick Macklem 	NFSLOCKNAMEID();
3840f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_nfsuserd) != RUNNING) {
38419ec7b004SRick Macklem 		NFSUNLOCKNAMEID();
38429ec7b004SRick Macklem 		return;
38439ec7b004SRick Macklem 	}
3844f0db2b60SRick Macklem 	NFSD_VNET(nfsrv_nfsuserd) = STARTSTOP;
3845e1cda5eeSRick Macklem 	/* Wait for all upcalls to complete. */
3846f0db2b60SRick Macklem 	while (NFSD_VNET(nfsrv_userdupcalls) > 0)
3847f0db2b60SRick Macklem 		msleep(&NFSD_VNET(nfsrv_userdupcalls), NFSNAMEIDMUTEXPTR, PVFS,
3848e1cda5eeSRick Macklem 		    "nfsupcalls", 0);
38499ec7b004SRick Macklem 	NFSUNLOCKNAMEID();
3850f0db2b60SRick Macklem 	newnfs_disconnect(NULL, &NFSD_VNET(nfsrv_nfsuserdsock));
3851f0db2b60SRick Macklem 	free(NFSD_VNET(nfsrv_nfsuserdsock).nr_nam, M_SONAME);
3852e1cda5eeSRick Macklem 	NFSLOCKNAMEID();
3853f0db2b60SRick Macklem 	NFSD_VNET(nfsrv_nfsuserd) = NOTRUNNING;
3854e1cda5eeSRick Macklem 	NFSUNLOCKNAMEID();
38559ec7b004SRick Macklem }
38569ec7b004SRick Macklem 
38579ec7b004SRick Macklem /*
38589ec7b004SRick Macklem  * Do upcalls to the nfsuserd, for cache misses of the owner/ownergroup
38599ec7b004SRick Macklem  * name<-->id cache.
38609ec7b004SRick Macklem  * Returns 0 upon success, non-zero otherwise.
38619ec7b004SRick Macklem  */
38629ec7b004SRick Macklem static int
nfsrv_getuser(int procnum,uid_t uid,gid_t gid,char * name)3863f32bf292SEdward Tomasz Napierala nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name)
38649ec7b004SRick Macklem {
38659ec7b004SRick Macklem 	u_int32_t *tl;
38669ec7b004SRick Macklem 	struct nfsrv_descript *nd;
38679ec7b004SRick Macklem 	int len;
38689ec7b004SRick Macklem 	struct nfsrv_descript nfsd;
38699ec7b004SRick Macklem 	struct ucred *cred;
38709ec7b004SRick Macklem 	int error;
38719ec7b004SRick Macklem 
38729ec7b004SRick Macklem 	NFSLOCKNAMEID();
3873f0db2b60SRick Macklem 	if (NFSD_VNET(nfsrv_nfsuserd) != RUNNING) {
38749ec7b004SRick Macklem 		NFSUNLOCKNAMEID();
3875a9285ae5SZack Kirsch 		error = EPERM;
3876a9285ae5SZack Kirsch 		goto out;
38779ec7b004SRick Macklem 	}
3878e1cda5eeSRick Macklem 	/*
3879e1cda5eeSRick Macklem 	 * Maintain a count of upcalls in progress, so that nfsrv_X()
3880e1cda5eeSRick Macklem 	 * can wait until no upcalls are in progress.
3881e1cda5eeSRick Macklem 	 */
3882f0db2b60SRick Macklem 	NFSD_VNET(nfsrv_userdupcalls)++;
38839ec7b004SRick Macklem 	NFSUNLOCKNAMEID();
3884f0db2b60SRick Macklem 	KASSERT(NFSD_VNET(nfsrv_userdupcalls) > 0,
3885e1cda5eeSRick Macklem 	    ("nfsrv_getuser: non-positive upcalls"));
38869ec7b004SRick Macklem 	nd = &nfsd;
38879ec7b004SRick Macklem 	cred = newnfs_getcred();
38889ec7b004SRick Macklem 	nd->nd_flag = ND_GSSINITREPLY;
38899ec7b004SRick Macklem 	nfsrvd_rephead(nd);
38909ec7b004SRick Macklem 
38919ec7b004SRick Macklem 	nd->nd_procnum = procnum;
38929ec7b004SRick Macklem 	if (procnum == RPCNFSUSERD_GETUID || procnum == RPCNFSUSERD_GETGID) {
38939ec7b004SRick Macklem 		NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
38949ec7b004SRick Macklem 		if (procnum == RPCNFSUSERD_GETUID)
38959ec7b004SRick Macklem 			*tl = txdr_unsigned(uid);
38969ec7b004SRick Macklem 		else
38979ec7b004SRick Macklem 			*tl = txdr_unsigned(gid);
38989ec7b004SRick Macklem 	} else {
38999ec7b004SRick Macklem 		len = strlen(name);
39009ec7b004SRick Macklem 		(void) nfsm_strtom(nd, name, len);
39019ec7b004SRick Macklem 	}
3902f0db2b60SRick Macklem 	error = newnfs_request(nd, NULL, NULL, &NFSD_VNET(nfsrv_nfsuserdsock),
3903f0db2b60SRick Macklem 	    NULL, NULL, cred, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, NULL, 0,
3904f0db2b60SRick Macklem 	    NULL, NULL);
3905e1cda5eeSRick Macklem 	NFSLOCKNAMEID();
3906f0db2b60SRick Macklem 	if (--NFSD_VNET(nfsrv_userdupcalls) == 0 &&
3907f0db2b60SRick Macklem 	    NFSD_VNET(nfsrv_nfsuserd) == STARTSTOP)
3908f0db2b60SRick Macklem 		wakeup(&NFSD_VNET(nfsrv_userdupcalls));
3909e1cda5eeSRick Macklem 	NFSUNLOCKNAMEID();
39109ec7b004SRick Macklem 	NFSFREECRED(cred);
39119ec7b004SRick Macklem 	if (!error) {
3912c948a17aSRick Macklem 		m_freem(nd->nd_mrep);
39139ec7b004SRick Macklem 		error = nd->nd_repstat;
39149ec7b004SRick Macklem 	}
3915a9285ae5SZack Kirsch out:
3916a9285ae5SZack Kirsch 	NFSEXITCODE(error);
39179ec7b004SRick Macklem 	return (error);
39189ec7b004SRick Macklem }
39199ec7b004SRick Macklem 
39209ec7b004SRick Macklem /*
39219ec7b004SRick Macklem  * This function is called from the nfssvc(2) system call, to update the
39229ec7b004SRick Macklem  * kernel user/group name list(s) for the V4 owner and ownergroup attributes.
39239ec7b004SRick Macklem  */
3924b9cc3262SRyan Moeller int
nfssvc_idname(struct nfsd_idargs * nidp)39259ec7b004SRick Macklem nfssvc_idname(struct nfsd_idargs *nidp)
39269ec7b004SRick Macklem {
39279ec7b004SRick Macklem 	struct nfsusrgrp *nusrp, *usrp, *newusrp;
392884be7e09SRick Macklem 	struct nfsrv_lughash *hp_name, *hp_idnum, *thp;
392984be7e09SRick Macklem 	int i, group_locked, groupname_locked, user_locked, username_locked;
39309ec7b004SRick Macklem 	int error = 0;
39319ec7b004SRick Macklem 	u_char *cp;
393284be7e09SRick Macklem 	gid_t *grps;
393384be7e09SRick Macklem 	struct ucred *cr;
393484be7e09SRick Macklem 	static int onethread = 0;
393584be7e09SRick Macklem 	static time_t lasttime = 0;
39369ec7b004SRick Macklem 
39378edac6eeSEd Maste 	if (nidp->nid_namelen <= 0 || nidp->nid_namelen > MAXHOSTNAMELEN) {
39388edac6eeSEd Maste 		error = EINVAL;
39398edac6eeSEd Maste 		goto out;
39408edac6eeSEd Maste 	}
39419ec7b004SRick Macklem 	if (nidp->nid_flag & NFSID_INITIALIZE) {
394284be7e09SRick Macklem 		cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
3943e4a458bbSRick Macklem 		error = copyin(nidp->nid_name, cp, nidp->nid_namelen);
394484be7e09SRick Macklem 		if (error != 0) {
394584be7e09SRick Macklem 			free(cp, M_NFSSTRING);
394684be7e09SRick Macklem 			goto out;
39479ec7b004SRick Macklem 		}
3948f0db2b60SRick Macklem 		if (atomic_cmpset_acq_int(&NFSD_VNET(nfsrv_dnsnamelen), 0, 0) ==
3949f0db2b60SRick Macklem 		    0) {
395084be7e09SRick Macklem 			/*
395184be7e09SRick Macklem 			 * Free up all the old stuff and reinitialize hash
395284be7e09SRick Macklem 			 * lists.  All mutexes for both lists must be locked,
395384be7e09SRick Macklem 			 * with the user/group name ones before the uid/gid
395484be7e09SRick Macklem 			 * ones, to avoid a LOR.
395584be7e09SRick Macklem 			 */
395684be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3957f0db2b60SRick Macklem 				mtx_lock(&NFSD_VNET(nfsusernamehash)[i].mtx);
395884be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3959f0db2b60SRick Macklem 				mtx_lock(&NFSD_VNET(nfsuserhash)[i].mtx);
396084be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
396184be7e09SRick Macklem 				TAILQ_FOREACH_SAFE(usrp,
3962f0db2b60SRick Macklem 				    &NFSD_VNET(nfsuserhash)[i].lughead, lug_numhash, nusrp)
396384be7e09SRick Macklem 					nfsrv_removeuser(usrp, 1);
396484be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3965f0db2b60SRick Macklem 				mtx_unlock(&NFSD_VNET(nfsuserhash)[i].mtx);
396684be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3967f0db2b60SRick Macklem 				mtx_unlock(&NFSD_VNET(nfsusernamehash)[i].mtx);
396884be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3969f0db2b60SRick Macklem 				mtx_lock(&NFSD_VNET(nfsgroupnamehash)[i].mtx);
397084be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3971f0db2b60SRick Macklem 				mtx_lock(&NFSD_VNET(nfsgrouphash)[i].mtx);
397284be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
397384be7e09SRick Macklem 				TAILQ_FOREACH_SAFE(usrp,
3974f0db2b60SRick Macklem 				    &NFSD_VNET(nfsgrouphash)[i].lughead, lug_numhash,
397584be7e09SRick Macklem 				    nusrp)
397684be7e09SRick Macklem 					nfsrv_removeuser(usrp, 0);
397784be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3978f0db2b60SRick Macklem 				mtx_unlock(&NFSD_VNET(nfsgrouphash)[i].mtx);
397984be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3980f0db2b60SRick Macklem 				mtx_unlock(&NFSD_VNET(nfsgroupnamehash)[i].mtx);
3981f0db2b60SRick Macklem 			free(NFSD_VNET(nfsrv_dnsname), M_NFSSTRING);
3982f0db2b60SRick Macklem 			NFSD_VNET(nfsrv_dnsname) = NULL;
39839ec7b004SRick Macklem 		}
3984f0db2b60SRick Macklem 		if (NFSD_VNET(nfsuserhash) == NULL) {
398584be7e09SRick Macklem 			/* Allocate the hash tables. */
3986f0db2b60SRick Macklem 			NFSD_VNET(nfsuserhash) = malloc(sizeof(struct nfsrv_lughash) *
398784be7e09SRick Macklem 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
398884be7e09SRick Macklem 			    M_ZERO);
398984be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3990f0db2b60SRick Macklem 				mtx_init(&NFSD_VNET(nfsuserhash)[i].mtx, "nfsuidhash",
399184be7e09SRick Macklem 				    NULL, MTX_DEF | MTX_DUPOK);
3992f0db2b60SRick Macklem 			NFSD_VNET(nfsusernamehash) = malloc(sizeof(struct nfsrv_lughash) *
399384be7e09SRick Macklem 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
399484be7e09SRick Macklem 			    M_ZERO);
399584be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
3996f0db2b60SRick Macklem 				mtx_init(&NFSD_VNET(nfsusernamehash)[i].mtx,
399784be7e09SRick Macklem 				    "nfsusrhash", NULL, MTX_DEF |
399884be7e09SRick Macklem 				    MTX_DUPOK);
3999f0db2b60SRick Macklem 			NFSD_VNET(nfsgrouphash) = malloc(sizeof(struct nfsrv_lughash) *
400084be7e09SRick Macklem 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
400184be7e09SRick Macklem 			    M_ZERO);
400284be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
4003f0db2b60SRick Macklem 				mtx_init(&NFSD_VNET(nfsgrouphash)[i].mtx, "nfsgidhash",
400484be7e09SRick Macklem 				    NULL, MTX_DEF | MTX_DUPOK);
4005f0db2b60SRick Macklem 			NFSD_VNET(nfsgroupnamehash) = malloc(sizeof(struct nfsrv_lughash) *
400684be7e09SRick Macklem 			    nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
400784be7e09SRick Macklem 			    M_ZERO);
400884be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
4009f0db2b60SRick Macklem 			    mtx_init(&NFSD_VNET(nfsgroupnamehash)[i].mtx,
401084be7e09SRick Macklem 			    "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
401184be7e09SRick Macklem 		}
401284be7e09SRick Macklem 		/* (Re)initialize the list heads. */
401384be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4014f0db2b60SRick Macklem 			TAILQ_INIT(&NFSD_VNET(nfsuserhash)[i].lughead);
401584be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4016f0db2b60SRick Macklem 			TAILQ_INIT(&NFSD_VNET(nfsusernamehash)[i].lughead);
401784be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4018f0db2b60SRick Macklem 			TAILQ_INIT(&NFSD_VNET(nfsgrouphash)[i].lughead);
401984be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4020f0db2b60SRick Macklem 			TAILQ_INIT(&NFSD_VNET(nfsgroupnamehash)[i].lughead);
40219ec7b004SRick Macklem 
40229ec7b004SRick Macklem 		/*
40239ec7b004SRick Macklem 		 * Put name in "DNS" string.
40249ec7b004SRick Macklem 		 */
4025f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_dnsname) = cp;
4026f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_defaultuid) = nidp->nid_uid;
4027f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_defaultgid) = nidp->nid_gid;
4028f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_usercnt) = 0;
4029f0db2b60SRick Macklem 		NFSD_VNET(nfsrv_usermax) = nidp->nid_usermax;
4030f0db2b60SRick Macklem 		atomic_store_rel_int(&NFSD_VNET(nfsrv_dnsnamelen),
4031f0db2b60SRick Macklem 		    nidp->nid_namelen);
4032a9285ae5SZack Kirsch 		goto out;
40339ec7b004SRick Macklem 	}
40349ec7b004SRick Macklem 
40359ec7b004SRick Macklem 	/*
40369ec7b004SRick Macklem 	 * malloc the new one now, so any potential sleep occurs before
40379ec7b004SRick Macklem 	 * manipulation of the lists.
40389ec7b004SRick Macklem 	 */
403984be7e09SRick Macklem 	newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
404084be7e09SRick Macklem 	    M_NFSUSERGROUP, M_WAITOK | M_ZERO);
4041e4a458bbSRick Macklem 	error = copyin(nidp->nid_name, newusrp->lug_name,
40429ec7b004SRick Macklem 	    nidp->nid_namelen);
404384be7e09SRick Macklem 	if (error == 0 && nidp->nid_ngroup > 0 &&
404484be7e09SRick Macklem 	    (nidp->nid_flag & NFSID_ADDUID) != 0) {
404584be7e09SRick Macklem 		grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
404684be7e09SRick Macklem 		    M_WAITOK);
4047e4a458bbSRick Macklem 		error = copyin(nidp->nid_grps, grps,
404884be7e09SRick Macklem 		    sizeof(gid_t) * nidp->nid_ngroup);
404984be7e09SRick Macklem 		if (error == 0) {
405084be7e09SRick Macklem 			/*
405184be7e09SRick Macklem 			 * Create a credential just like svc_getcred(),
405284be7e09SRick Macklem 			 * but using the group list provided.
405384be7e09SRick Macklem 			 */
405484be7e09SRick Macklem 			cr = crget();
405584be7e09SRick Macklem 			cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
4056cfbe7a62SOlivier Certner 			crsetgroups_fallback(cr, nidp->nid_ngroup, grps,
40575169d430SOlivier Certner 			    GID_NOGROUP);
4058cfbe7a62SOlivier Certner 			cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
4059f0db2b60SRick Macklem 			cr->cr_prison = curthread->td_ucred->cr_prison;
406084be7e09SRick Macklem 			prison_hold(cr->cr_prison);
406184be7e09SRick Macklem #ifdef MAC
406284be7e09SRick Macklem 			mac_cred_associate_nfsd(cr);
406384be7e09SRick Macklem #endif
406484be7e09SRick Macklem 			newusrp->lug_cred = cr;
406584be7e09SRick Macklem 		}
406684be7e09SRick Macklem 		free(grps, M_TEMP);
406784be7e09SRick Macklem 	}
40689ec7b004SRick Macklem 	if (error) {
406984be7e09SRick Macklem 		free(newusrp, M_NFSUSERGROUP);
4070a9285ae5SZack Kirsch 		goto out;
40719ec7b004SRick Macklem 	}
40729ec7b004SRick Macklem 	newusrp->lug_namelen = nidp->nid_namelen;
40739ec7b004SRick Macklem 
407484be7e09SRick Macklem 	/*
407584be7e09SRick Macklem 	 * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
407684be7e09SRick Macklem 	 * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
407784be7e09SRick Macklem 	 * The flags user_locked, username_locked, group_locked and
407884be7e09SRick Macklem 	 * groupname_locked are set to indicate all of those hash lists are
407984be7e09SRick Macklem 	 * locked. hp_name != NULL  and hp_idnum != NULL indicates that
408084be7e09SRick Macklem 	 * the respective one mutex is locked.
408184be7e09SRick Macklem 	 */
408284be7e09SRick Macklem 	user_locked = username_locked = group_locked = groupname_locked = 0;
408384be7e09SRick Macklem 	hp_name = hp_idnum = NULL;
408484be7e09SRick Macklem 
40859ec7b004SRick Macklem 	/*
40869ec7b004SRick Macklem 	 * Delete old entries, as required.
40879ec7b004SRick Macklem 	 */
40889ec7b004SRick Macklem 	if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
408984be7e09SRick Macklem 		/* Must lock all username hash lists first, to avoid a LOR. */
409084be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4091f0db2b60SRick Macklem 			mtx_lock(&NFSD_VNET(nfsusernamehash)[i].mtx);
409284be7e09SRick Macklem 		username_locked = 1;
409384be7e09SRick Macklem 		hp_idnum = NFSUSERHASH(nidp->nid_uid);
409484be7e09SRick Macklem 		mtx_lock(&hp_idnum->mtx);
409584be7e09SRick Macklem 		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
409684be7e09SRick Macklem 		    nusrp) {
40979ec7b004SRick Macklem 			if (usrp->lug_uid == nidp->nid_uid)
409884be7e09SRick Macklem 				nfsrv_removeuser(usrp, 1);
40999ec7b004SRick Macklem 		}
410084be7e09SRick Macklem 	} else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
410184be7e09SRick Macklem 		hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
410284be7e09SRick Macklem 		    newusrp->lug_namelen);
410384be7e09SRick Macklem 		mtx_lock(&hp_name->mtx);
410484be7e09SRick Macklem 		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
410584be7e09SRick Macklem 		    nusrp) {
41069ec7b004SRick Macklem 			if (usrp->lug_namelen == newusrp->lug_namelen &&
41079ec7b004SRick Macklem 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
410884be7e09SRick Macklem 			    usrp->lug_namelen)) {
410984be7e09SRick Macklem 				thp = NFSUSERHASH(usrp->lug_uid);
411084be7e09SRick Macklem 				mtx_lock(&thp->mtx);
411184be7e09SRick Macklem 				nfsrv_removeuser(usrp, 1);
411284be7e09SRick Macklem 				mtx_unlock(&thp->mtx);
41139ec7b004SRick Macklem 			}
41149ec7b004SRick Macklem 		}
411584be7e09SRick Macklem 		hp_idnum = NFSUSERHASH(nidp->nid_uid);
411684be7e09SRick Macklem 		mtx_lock(&hp_idnum->mtx);
411784be7e09SRick Macklem 	} else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
411884be7e09SRick Macklem 		/* Must lock all groupname hash lists first, to avoid a LOR. */
411984be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4120f0db2b60SRick Macklem 			mtx_lock(&NFSD_VNET(nfsgroupnamehash)[i].mtx);
412184be7e09SRick Macklem 		groupname_locked = 1;
412284be7e09SRick Macklem 		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
412384be7e09SRick Macklem 		mtx_lock(&hp_idnum->mtx);
412484be7e09SRick Macklem 		TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
412584be7e09SRick Macklem 		    nusrp) {
41269ec7b004SRick Macklem 			if (usrp->lug_gid == nidp->nid_gid)
412784be7e09SRick Macklem 				nfsrv_removeuser(usrp, 0);
41289ec7b004SRick Macklem 		}
412984be7e09SRick Macklem 	} else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
413084be7e09SRick Macklem 		hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
413184be7e09SRick Macklem 		    newusrp->lug_namelen);
413284be7e09SRick Macklem 		mtx_lock(&hp_name->mtx);
413384be7e09SRick Macklem 		TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
413484be7e09SRick Macklem 		    nusrp) {
41359ec7b004SRick Macklem 			if (usrp->lug_namelen == newusrp->lug_namelen &&
41369ec7b004SRick Macklem 			    !NFSBCMP(usrp->lug_name, newusrp->lug_name,
413784be7e09SRick Macklem 			    usrp->lug_namelen)) {
413884be7e09SRick Macklem 				thp = NFSGROUPHASH(usrp->lug_gid);
413984be7e09SRick Macklem 				mtx_lock(&thp->mtx);
414084be7e09SRick Macklem 				nfsrv_removeuser(usrp, 0);
414184be7e09SRick Macklem 				mtx_unlock(&thp->mtx);
41429ec7b004SRick Macklem 			}
41439ec7b004SRick Macklem 		}
414484be7e09SRick Macklem 		hp_idnum = NFSGROUPHASH(nidp->nid_gid);
414584be7e09SRick Macklem 		mtx_lock(&hp_idnum->mtx);
41469ec7b004SRick Macklem 	}
41479ec7b004SRick Macklem 
41489ec7b004SRick Macklem 	/*
41499ec7b004SRick Macklem 	 * Now, we can add the new one.
41509ec7b004SRick Macklem 	 */
41519ec7b004SRick Macklem 	if (nidp->nid_usertimeout)
41529ec7b004SRick Macklem 		newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
41539ec7b004SRick Macklem 	else
41549ec7b004SRick Macklem 		newusrp->lug_expiry = NFSD_MONOSEC + 5;
41559ec7b004SRick Macklem 	if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
41569ec7b004SRick Macklem 		newusrp->lug_uid = nidp->nid_uid;
415784be7e09SRick Macklem 		thp = NFSUSERHASH(newusrp->lug_uid);
415884be7e09SRick Macklem 		mtx_assert(&thp->mtx, MA_OWNED);
415984be7e09SRick Macklem 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
416084be7e09SRick Macklem 		thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
416184be7e09SRick Macklem 		mtx_assert(&thp->mtx, MA_OWNED);
416284be7e09SRick Macklem 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
4163f0db2b60SRick Macklem 		atomic_add_int(&NFSD_VNET(nfsrv_usercnt), 1);
41649ec7b004SRick Macklem 	} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
41659ec7b004SRick Macklem 		newusrp->lug_gid = nidp->nid_gid;
416684be7e09SRick Macklem 		thp = NFSGROUPHASH(newusrp->lug_gid);
416784be7e09SRick Macklem 		mtx_assert(&thp->mtx, MA_OWNED);
416884be7e09SRick Macklem 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
416984be7e09SRick Macklem 		thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
417084be7e09SRick Macklem 		mtx_assert(&thp->mtx, MA_OWNED);
417184be7e09SRick Macklem 		TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
4172f0db2b60SRick Macklem 		atomic_add_int(&NFSD_VNET(nfsrv_usercnt), 1);
417384be7e09SRick Macklem 	} else {
417484be7e09SRick Macklem 		if (newusrp->lug_cred != NULL)
417584be7e09SRick Macklem 			crfree(newusrp->lug_cred);
417684be7e09SRick Macklem 		free(newusrp, M_NFSUSERGROUP);
417784be7e09SRick Macklem 	}
417884be7e09SRick Macklem 
417984be7e09SRick Macklem 	/*
418084be7e09SRick Macklem 	 * Once per second, allow one thread to trim the cache.
418184be7e09SRick Macklem 	 */
418284be7e09SRick Macklem 	if (lasttime < NFSD_MONOSEC &&
418384be7e09SRick Macklem 	    atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
418484be7e09SRick Macklem 		/*
418584be7e09SRick Macklem 		 * First, unlock the single mutexes, so that all entries
418684be7e09SRick Macklem 		 * can be locked and any LOR is avoided.
418784be7e09SRick Macklem 		 */
418884be7e09SRick Macklem 		if (hp_name != NULL) {
418984be7e09SRick Macklem 			mtx_unlock(&hp_name->mtx);
419084be7e09SRick Macklem 			hp_name = NULL;
419184be7e09SRick Macklem 		}
419284be7e09SRick Macklem 		if (hp_idnum != NULL) {
419384be7e09SRick Macklem 			mtx_unlock(&hp_idnum->mtx);
419484be7e09SRick Macklem 			hp_idnum = NULL;
419584be7e09SRick Macklem 		}
419684be7e09SRick Macklem 
419784be7e09SRick Macklem 		if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
419884be7e09SRick Macklem 		    NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
419984be7e09SRick Macklem 			if (username_locked == 0) {
420084be7e09SRick Macklem 				for (i = 0; i < nfsrv_lughashsize; i++)
4201f0db2b60SRick Macklem 					mtx_lock(&NFSD_VNET(nfsusernamehash)[i].mtx);
420284be7e09SRick Macklem 				username_locked = 1;
420384be7e09SRick Macklem 			}
420484be7e09SRick Macklem 			KASSERT(user_locked == 0,
420584be7e09SRick Macklem 			    ("nfssvc_idname: user_locked"));
420684be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
4207f0db2b60SRick Macklem 				mtx_lock(&NFSD_VNET(nfsuserhash)[i].mtx);
420884be7e09SRick Macklem 			user_locked = 1;
420984be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++) {
421084be7e09SRick Macklem 				TAILQ_FOREACH_SAFE(usrp,
4211f0db2b60SRick Macklem 				    &NFSD_VNET(nfsuserhash)[i].lughead, lug_numhash,
421284be7e09SRick Macklem 				    nusrp)
421384be7e09SRick Macklem 					if (usrp->lug_expiry < NFSD_MONOSEC)
421484be7e09SRick Macklem 						nfsrv_removeuser(usrp, 1);
421584be7e09SRick Macklem 			}
421684be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++) {
421784be7e09SRick Macklem 				/*
421884be7e09SRick Macklem 				 * Trim the cache using an approximate LRU
421984be7e09SRick Macklem 				 * algorithm.  This code deletes the least
422084be7e09SRick Macklem 				 * recently used entry on each hash list.
422184be7e09SRick Macklem 				 */
4222f0db2b60SRick Macklem 				if (NFSD_VNET(nfsrv_usercnt) <= NFSD_VNET(nfsrv_usermax))
422384be7e09SRick Macklem 					break;
4224f0db2b60SRick Macklem 				usrp = TAILQ_FIRST(&NFSD_VNET(nfsuserhash)[i].lughead);
422584be7e09SRick Macklem 				if (usrp != NULL)
422684be7e09SRick Macklem 					nfsrv_removeuser(usrp, 1);
422784be7e09SRick Macklem 			}
422884be7e09SRick Macklem 		} else {
422984be7e09SRick Macklem 			if (groupname_locked == 0) {
423084be7e09SRick Macklem 				for (i = 0; i < nfsrv_lughashsize; i++)
4231f0db2b60SRick Macklem 					mtx_lock(&NFSD_VNET(nfsgroupnamehash)[i].mtx);
423284be7e09SRick Macklem 				groupname_locked = 1;
423384be7e09SRick Macklem 			}
423484be7e09SRick Macklem 			KASSERT(group_locked == 0,
423584be7e09SRick Macklem 			    ("nfssvc_idname: group_locked"));
423684be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++)
4237f0db2b60SRick Macklem 				mtx_lock(&NFSD_VNET(nfsgrouphash)[i].mtx);
423884be7e09SRick Macklem 			group_locked = 1;
423984be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++) {
424084be7e09SRick Macklem 				TAILQ_FOREACH_SAFE(usrp,
4241f0db2b60SRick Macklem 				    &NFSD_VNET(nfsgrouphash)[i].lughead, lug_numhash,
424284be7e09SRick Macklem 				    nusrp)
424384be7e09SRick Macklem 					if (usrp->lug_expiry < NFSD_MONOSEC)
424484be7e09SRick Macklem 						nfsrv_removeuser(usrp, 0);
424584be7e09SRick Macklem 			}
424684be7e09SRick Macklem 			for (i = 0; i < nfsrv_lughashsize; i++) {
424784be7e09SRick Macklem 				/*
424884be7e09SRick Macklem 				 * Trim the cache using an approximate LRU
424984be7e09SRick Macklem 				 * algorithm.  This code deletes the least
425084be7e09SRick Macklem 				 * recently user entry on each hash list.
425184be7e09SRick Macklem 				 */
4252f0db2b60SRick Macklem 				if (NFSD_VNET(nfsrv_usercnt) <= NFSD_VNET(nfsrv_usermax))
425384be7e09SRick Macklem 					break;
4254f0db2b60SRick Macklem 				usrp = TAILQ_FIRST(&NFSD_VNET(nfsgrouphash)[i].lughead);
425584be7e09SRick Macklem 				if (usrp != NULL)
425684be7e09SRick Macklem 					nfsrv_removeuser(usrp, 0);
425784be7e09SRick Macklem 			}
425884be7e09SRick Macklem 		}
425984be7e09SRick Macklem 		lasttime = NFSD_MONOSEC;
426084be7e09SRick Macklem 		atomic_store_rel_int(&onethread, 0);
426184be7e09SRick Macklem 	}
426284be7e09SRick Macklem 
426384be7e09SRick Macklem 	/* Now, unlock all locked mutexes. */
426484be7e09SRick Macklem 	if (hp_idnum != NULL)
426584be7e09SRick Macklem 		mtx_unlock(&hp_idnum->mtx);
426684be7e09SRick Macklem 	if (hp_name != NULL)
426784be7e09SRick Macklem 		mtx_unlock(&hp_name->mtx);
426884be7e09SRick Macklem 	if (user_locked != 0)
426984be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4270f0db2b60SRick Macklem 			mtx_unlock(&NFSD_VNET(nfsuserhash)[i].mtx);
427184be7e09SRick Macklem 	if (username_locked != 0)
427284be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4273f0db2b60SRick Macklem 			mtx_unlock(&NFSD_VNET(nfsusernamehash)[i].mtx);
427484be7e09SRick Macklem 	if (group_locked != 0)
427584be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4276f0db2b60SRick Macklem 			mtx_unlock(&NFSD_VNET(nfsgrouphash)[i].mtx);
427784be7e09SRick Macklem 	if (groupname_locked != 0)
427884be7e09SRick Macklem 		for (i = 0; i < nfsrv_lughashsize; i++)
4279f0db2b60SRick Macklem 			mtx_unlock(&NFSD_VNET(nfsgroupnamehash)[i].mtx);
4280a9285ae5SZack Kirsch out:
4281a9285ae5SZack Kirsch 	NFSEXITCODE(error);
42829ec7b004SRick Macklem 	return (error);
42839ec7b004SRick Macklem }
42849ec7b004SRick Macklem 
42859ec7b004SRick Macklem /*
42869ec7b004SRick Macklem  * Remove a user/group name element.
42879ec7b004SRick Macklem  */
42889ec7b004SRick Macklem static void
nfsrv_removeuser(struct nfsusrgrp * usrp,int isuser)428984be7e09SRick Macklem nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
42909ec7b004SRick Macklem {
429184be7e09SRick Macklem 	struct nfsrv_lughash *hp;
42929ec7b004SRick Macklem 
429384be7e09SRick Macklem 	if (isuser != 0) {
429484be7e09SRick Macklem 		hp = NFSUSERHASH(usrp->lug_uid);
429584be7e09SRick Macklem 		mtx_assert(&hp->mtx, MA_OWNED);
429684be7e09SRick Macklem 		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
429784be7e09SRick Macklem 		hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
429884be7e09SRick Macklem 		mtx_assert(&hp->mtx, MA_OWNED);
429984be7e09SRick Macklem 		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
430084be7e09SRick Macklem 	} else {
430184be7e09SRick Macklem 		hp = NFSGROUPHASH(usrp->lug_gid);
430284be7e09SRick Macklem 		mtx_assert(&hp->mtx, MA_OWNED);
430384be7e09SRick Macklem 		TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
430484be7e09SRick Macklem 		hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
430584be7e09SRick Macklem 		mtx_assert(&hp->mtx, MA_OWNED);
430684be7e09SRick Macklem 		TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
430784be7e09SRick Macklem 	}
4308f0db2b60SRick Macklem 	atomic_add_int(&NFSD_VNET(nfsrv_usercnt), -1);
430984be7e09SRick Macklem 	if (usrp->lug_cred != NULL)
431084be7e09SRick Macklem 		crfree(usrp->lug_cred);
431184be7e09SRick Macklem 	free(usrp, M_NFSUSERGROUP);
43129ec7b004SRick Macklem }
43139ec7b004SRick Macklem 
43149ec7b004SRick Macklem /*
431565171ebbSRick Macklem  * Free up all the allocations related to the name<-->id cache.
431665171ebbSRick Macklem  * This function should only be called when the nfsuserd daemon isn't
431765171ebbSRick Macklem  * running, since it doesn't do any locking.
4318f0db2b60SRick Macklem  * This function is meant to be called when a vnet jail is destroyed.
431965171ebbSRick Macklem  */
4320b9cc3262SRyan Moeller void
nfsrv_cleanusergroup(void)432165171ebbSRick Macklem nfsrv_cleanusergroup(void)
432265171ebbSRick Macklem {
432365171ebbSRick Macklem 	struct nfsrv_lughash *hp, *hp2;
432465171ebbSRick Macklem 	struct nfsusrgrp *nusrp, *usrp;
432565171ebbSRick Macklem 	int i;
432665171ebbSRick Macklem 
4327f0db2b60SRick Macklem 	if (NFSD_VNET(nfsuserhash) == NULL)
432865171ebbSRick Macklem 		return;
432965171ebbSRick Macklem 
433065171ebbSRick Macklem 	for (i = 0; i < nfsrv_lughashsize; i++) {
4331f0db2b60SRick Macklem 		hp = &NFSD_VNET(nfsuserhash)[i];
433265171ebbSRick Macklem 		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
433365171ebbSRick Macklem 			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
433465171ebbSRick Macklem 			hp2 = NFSUSERNAMEHASH(usrp->lug_name,
433565171ebbSRick Macklem 			    usrp->lug_namelen);
433665171ebbSRick Macklem 			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
433765171ebbSRick Macklem 			if (usrp->lug_cred != NULL)
433865171ebbSRick Macklem 				crfree(usrp->lug_cred);
433965171ebbSRick Macklem 			free(usrp, M_NFSUSERGROUP);
434065171ebbSRick Macklem 		}
4341f0db2b60SRick Macklem 		hp = &NFSD_VNET(nfsgrouphash)[i];
434265171ebbSRick Macklem 		TAILQ_FOREACH_SAFE(usrp, &hp->lughead, lug_numhash, nusrp) {
434365171ebbSRick Macklem 			TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
434465171ebbSRick Macklem 			hp2 = NFSGROUPNAMEHASH(usrp->lug_name,
434565171ebbSRick Macklem 			    usrp->lug_namelen);
434665171ebbSRick Macklem 			TAILQ_REMOVE(&hp2->lughead, usrp, lug_namehash);
434765171ebbSRick Macklem 			if (usrp->lug_cred != NULL)
434865171ebbSRick Macklem 				crfree(usrp->lug_cred);
434965171ebbSRick Macklem 			free(usrp, M_NFSUSERGROUP);
435065171ebbSRick Macklem 		}
4351f0db2b60SRick Macklem 		mtx_destroy(&NFSD_VNET(nfsuserhash)[i].mtx);
4352f0db2b60SRick Macklem 		mtx_destroy(&NFSD_VNET(nfsusernamehash)[i].mtx);
4353f0db2b60SRick Macklem 		mtx_destroy(&NFSD_VNET(nfsgroupnamehash)[i].mtx);
4354f0db2b60SRick Macklem 		mtx_destroy(&NFSD_VNET(nfsgrouphash)[i].mtx);
435565171ebbSRick Macklem 	}
4356f0db2b60SRick Macklem 	free(NFSD_VNET(nfsuserhash), M_NFSUSERGROUP);
4357f0db2b60SRick Macklem 	free(NFSD_VNET(nfsusernamehash), M_NFSUSERGROUP);
4358f0db2b60SRick Macklem 	free(NFSD_VNET(nfsgrouphash), M_NFSUSERGROUP);
4359f0db2b60SRick Macklem 	free(NFSD_VNET(nfsgroupnamehash), M_NFSUSERGROUP);
4360f0db2b60SRick Macklem 	free(NFSD_VNET(nfsrv_dnsname), M_NFSSTRING);
436165171ebbSRick Macklem }
436265171ebbSRick Macklem 
436365171ebbSRick Macklem /*
43649ec7b004SRick Macklem  * This function scans a byte string and checks for UTF-8 compliance.
43659ec7b004SRick Macklem  * It returns 0 if it conforms and NFSERR_INVAL if not.
43669ec7b004SRick Macklem  */
4367b9cc3262SRyan Moeller int
nfsrv_checkutf8(u_int8_t * cp,int len)43689ec7b004SRick Macklem nfsrv_checkutf8(u_int8_t *cp, int len)
43699ec7b004SRick Macklem {
43709ec7b004SRick Macklem 	u_int32_t val = 0x0;
43719ec7b004SRick Macklem 	int cnt = 0, gotd = 0, shift = 0;
43729ec7b004SRick Macklem 	u_int8_t byte;
43739ec7b004SRick Macklem 	static int utf8_shift[5] = { 7, 11, 16, 21, 26 };
4374a9285ae5SZack Kirsch 	int error = 0;
43759ec7b004SRick Macklem 
43769ec7b004SRick Macklem 	/*
43779ec7b004SRick Macklem 	 * Here are what the variables are used for:
43789ec7b004SRick Macklem 	 * val - the calculated value of a multibyte char, used to check
43799ec7b004SRick Macklem 	 *       that it was coded with the correct range
43809ec7b004SRick Macklem 	 * cnt - the number of 10xxxxxx bytes to follow
43819ec7b004SRick Macklem 	 * gotd - set for a char of Dxxx, so D800<->DFFF can be checked for
43829ec7b004SRick Macklem 	 * shift - lower order bits of range (ie. "val >> shift" should
43839ec7b004SRick Macklem 	 *       not be 0, in other words, dividing by the lower bound
43849ec7b004SRick Macklem 	 *       of the range should get a non-zero value)
43859ec7b004SRick Macklem 	 * byte - used to calculate cnt
43869ec7b004SRick Macklem 	 */
43879ec7b004SRick Macklem 	while (len > 0) {
43889ec7b004SRick Macklem 		if (cnt > 0) {
43899ec7b004SRick Macklem 			/* This handles the 10xxxxxx bytes */
43909ec7b004SRick Macklem 			if ((*cp & 0xc0) != 0x80 ||
4391a9285ae5SZack Kirsch 			    (gotd && (*cp & 0x20))) {
4392a9285ae5SZack Kirsch 				error = NFSERR_INVAL;
4393a9285ae5SZack Kirsch 				goto out;
4394a9285ae5SZack Kirsch 			}
43959ec7b004SRick Macklem 			gotd = 0;
43969ec7b004SRick Macklem 			val <<= 6;
43979ec7b004SRick Macklem 			val |= (*cp & 0x3f);
43989ec7b004SRick Macklem 			cnt--;
4399a9285ae5SZack Kirsch 			if (cnt == 0 && (val >> shift) == 0x0) {
4400a9285ae5SZack Kirsch 				error = NFSERR_INVAL;
4401a9285ae5SZack Kirsch 				goto out;
4402a9285ae5SZack Kirsch 			}
44039ec7b004SRick Macklem 		} else if (*cp & 0x80) {
44049ec7b004SRick Macklem 			/* first byte of multi byte char */
44059ec7b004SRick Macklem 			byte = *cp;
44069ec7b004SRick Macklem 			while ((byte & 0x40) && cnt < 6) {
44079ec7b004SRick Macklem 				cnt++;
44089ec7b004SRick Macklem 				byte <<= 1;
44099ec7b004SRick Macklem 			}
4410a9285ae5SZack Kirsch 			if (cnt == 0 || cnt == 6) {
4411a9285ae5SZack Kirsch 				error = NFSERR_INVAL;
4412a9285ae5SZack Kirsch 				goto out;
4413a9285ae5SZack Kirsch 			}
44149ec7b004SRick Macklem 			val = (*cp & (0x3f >> cnt));
44159ec7b004SRick Macklem 			shift = utf8_shift[cnt - 1];
44169ec7b004SRick Macklem 			if (cnt == 2 && val == 0xd)
44179ec7b004SRick Macklem 				/* Check for the 0xd800-0xdfff case */
44189ec7b004SRick Macklem 				gotd = 1;
44199ec7b004SRick Macklem 		}
44209ec7b004SRick Macklem 		cp++;
44219ec7b004SRick Macklem 		len--;
44229ec7b004SRick Macklem 	}
44239ec7b004SRick Macklem 	if (cnt > 0)
4424a9285ae5SZack Kirsch 		error = NFSERR_INVAL;
4425a9285ae5SZack Kirsch 
4426a9285ae5SZack Kirsch out:
4427a9285ae5SZack Kirsch 	NFSEXITCODE(error);
4428a9285ae5SZack Kirsch 	return (error);
44299ec7b004SRick Macklem }
44309ec7b004SRick Macklem 
44319ec7b004SRick Macklem /*
44329ec7b004SRick Macklem  * Parse the xdr for an NFSv4 FsLocations attribute. Return two malloc'd
44339ec7b004SRick Macklem  * strings, one with the root path in it and the other with the list of
44349ec7b004SRick Macklem  * locations. The list is in the same format as is found in nfr_refs.
44359ec7b004SRick Macklem  * It is a "," separated list of entries, where each of them is of the
44369ec7b004SRick Macklem  * form <server>:<rootpath>. For example
44379ec7b004SRick Macklem  * "nfsv4-test:/sub2,nfsv4-test2:/user/mnt,nfsv4-test2:/user/mnt2"
44389ec7b004SRick Macklem  * The nilp argument is set to 1 for the special case of a null fs_root
44399ec7b004SRick Macklem  * and an empty server list.
44409ec7b004SRick Macklem  * It returns NFSERR_BADXDR, if the xdr can't be parsed and returns the
44419ec7b004SRick Macklem  * number of xdr bytes parsed in sump.
44429ec7b004SRick Macklem  */
44439ec7b004SRick Macklem static int
nfsrv_getrefstr(struct nfsrv_descript * nd,u_char ** fsrootp,u_char ** srvp,int * sump,int * nilp)44449ec7b004SRick Macklem nfsrv_getrefstr(struct nfsrv_descript *nd, u_char **fsrootp, u_char **srvp,
44459ec7b004SRick Macklem     int *sump, int *nilp)
44469ec7b004SRick Macklem {
44479ec7b004SRick Macklem 	u_int32_t *tl;
44489ec7b004SRick Macklem 	u_char *cp = NULL, *cp2 = NULL, *cp3, *str;
4449a9285ae5SZack Kirsch 	int i, j, len, stringlen, cnt, slen, siz, xdrsum, error = 0, nsrv;
44509ec7b004SRick Macklem 	struct list {
44519ec7b004SRick Macklem 		SLIST_ENTRY(list) next;
44529ec7b004SRick Macklem 		int len;
44539ec7b004SRick Macklem 		u_char host[1];
44549ec7b004SRick Macklem 	} *lsp, *nlsp;
44559ec7b004SRick Macklem 	SLIST_HEAD(, list) head;
44569ec7b004SRick Macklem 
44579ec7b004SRick Macklem 	*fsrootp = NULL;
44589ec7b004SRick Macklem 	*srvp = NULL;
44599ec7b004SRick Macklem 	*nilp = 0;
44609ec7b004SRick Macklem 
44619ec7b004SRick Macklem 	/*
44629ec7b004SRick Macklem 	 * Get the fs_root path and check for the special case of null path
44639ec7b004SRick Macklem 	 * and 0 length server list.
44649ec7b004SRick Macklem 	 */
44659ec7b004SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
44669ec7b004SRick Macklem 	len = fxdr_unsigned(int, *tl);
4467a9285ae5SZack Kirsch 	if (len < 0 || len > 10240) {
4468a9285ae5SZack Kirsch 		error = NFSERR_BADXDR;
4469a9285ae5SZack Kirsch 		goto nfsmout;
4470a9285ae5SZack Kirsch 	}
44719ec7b004SRick Macklem 	if (len == 0) {
44729ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
4473a9285ae5SZack Kirsch 		if (*tl != 0) {
4474a9285ae5SZack Kirsch 			error = NFSERR_BADXDR;
4475a9285ae5SZack Kirsch 			goto nfsmout;
4476a9285ae5SZack Kirsch 		}
44779ec7b004SRick Macklem 		*nilp = 1;
44789ec7b004SRick Macklem 		*sump = 2 * NFSX_UNSIGNED;
4479a9285ae5SZack Kirsch 		error = 0;
4480a9285ae5SZack Kirsch 		goto nfsmout;
44819ec7b004SRick Macklem 	}
44829ec7b004SRick Macklem 	cp = malloc(len + 1, M_NFSSTRING, M_WAITOK);
44839ec7b004SRick Macklem 	error = nfsrv_mtostr(nd, cp, len);
44849ec7b004SRick Macklem 	if (!error) {
44859ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
44869ec7b004SRick Macklem 		cnt = fxdr_unsigned(int, *tl);
44879ec7b004SRick Macklem 		if (cnt <= 0)
44889ec7b004SRick Macklem 			error = NFSERR_BADXDR;
44899ec7b004SRick Macklem 	}
4490a9285ae5SZack Kirsch 	if (error)
4491a9285ae5SZack Kirsch 		goto nfsmout;
44929ec7b004SRick Macklem 
44939ec7b004SRick Macklem 	/*
44949ec7b004SRick Macklem 	 * Now, loop through the location list and make up the srvlist.
44959ec7b004SRick Macklem 	 */
44969ec7b004SRick Macklem 	xdrsum = (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
44979ec7b004SRick Macklem 	cp2 = cp3 = malloc(1024, M_NFSSTRING, M_WAITOK);
44989ec7b004SRick Macklem 	slen = 1024;
44999ec7b004SRick Macklem 	siz = 0;
45009ec7b004SRick Macklem 	for (i = 0; i < cnt; i++) {
45019ec7b004SRick Macklem 		SLIST_INIT(&head);
45029ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
45039ec7b004SRick Macklem 		nsrv = fxdr_unsigned(int, *tl);
45049ec7b004SRick Macklem 		if (nsrv <= 0) {
4505a9285ae5SZack Kirsch 			error = NFSERR_BADXDR;
4506a9285ae5SZack Kirsch 			goto nfsmout;
45079ec7b004SRick Macklem 		}
45089ec7b004SRick Macklem 
45099ec7b004SRick Macklem 		/*
45109ec7b004SRick Macklem 		 * Handle the first server by putting it in the srvstr.
45119ec7b004SRick Macklem 		 */
45129ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
45139ec7b004SRick Macklem 		len = fxdr_unsigned(int, *tl);
45149ec7b004SRick Macklem 		if (len <= 0 || len > 1024) {
4515a9285ae5SZack Kirsch 			error = NFSERR_BADXDR;
4516a9285ae5SZack Kirsch 			goto nfsmout;
45179ec7b004SRick Macklem 		}
45189ec7b004SRick Macklem 		nfsrv_refstrbigenough(siz + len + 3, &cp2, &cp3, &slen);
45199ec7b004SRick Macklem 		if (cp3 != cp2) {
45209ec7b004SRick Macklem 			*cp3++ = ',';
45219ec7b004SRick Macklem 			siz++;
45229ec7b004SRick Macklem 		}
45239ec7b004SRick Macklem 		error = nfsrv_mtostr(nd, cp3, len);
4524a9285ae5SZack Kirsch 		if (error)
4525a9285ae5SZack Kirsch 			goto nfsmout;
45269ec7b004SRick Macklem 		cp3 += len;
45279ec7b004SRick Macklem 		*cp3++ = ':';
45289ec7b004SRick Macklem 		siz += (len + 1);
45299ec7b004SRick Macklem 		xdrsum += (2 * NFSX_UNSIGNED) + NFSM_RNDUP(len);
45309ec7b004SRick Macklem 		for (j = 1; j < nsrv; j++) {
45319ec7b004SRick Macklem 			/*
45329ec7b004SRick Macklem 			 * Yuck, put them in an slist and process them later.
45339ec7b004SRick Macklem 			 */
45349ec7b004SRick Macklem 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
45359ec7b004SRick Macklem 			len = fxdr_unsigned(int, *tl);
45369ec7b004SRick Macklem 			if (len <= 0 || len > 1024) {
4537a9285ae5SZack Kirsch 				error = NFSERR_BADXDR;
4538a9285ae5SZack Kirsch 				goto nfsmout;
45399ec7b004SRick Macklem 			}
45409ec7b004SRick Macklem 			lsp = (struct list *)malloc(sizeof (struct list)
45419ec7b004SRick Macklem 			    + len, M_TEMP, M_WAITOK);
45429ec7b004SRick Macklem 			error = nfsrv_mtostr(nd, lsp->host, len);
4543a9285ae5SZack Kirsch 			if (error)
4544a9285ae5SZack Kirsch 				goto nfsmout;
45459ec7b004SRick Macklem 			xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
45469ec7b004SRick Macklem 			lsp->len = len;
45479ec7b004SRick Macklem 			SLIST_INSERT_HEAD(&head, lsp, next);
45489ec7b004SRick Macklem 		}
45499ec7b004SRick Macklem 
45509ec7b004SRick Macklem 		/*
45519ec7b004SRick Macklem 		 * Finally, we can get the path.
45529ec7b004SRick Macklem 		 */
45539ec7b004SRick Macklem 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
45549ec7b004SRick Macklem 		len = fxdr_unsigned(int, *tl);
45559ec7b004SRick Macklem 		if (len <= 0 || len > 1024) {
4556a9285ae5SZack Kirsch 			error = NFSERR_BADXDR;
4557a9285ae5SZack Kirsch 			goto nfsmout;
45589ec7b004SRick Macklem 		}
45599ec7b004SRick Macklem 		nfsrv_refstrbigenough(siz + len + 1, &cp2, &cp3, &slen);
45609ec7b004SRick Macklem 		error = nfsrv_mtostr(nd, cp3, len);
4561a9285ae5SZack Kirsch 		if (error)
4562a9285ae5SZack Kirsch 			goto nfsmout;
45639ec7b004SRick Macklem 		xdrsum += NFSX_UNSIGNED + NFSM_RNDUP(len);
45649ec7b004SRick Macklem 		str = cp3;
45659ec7b004SRick Macklem 		stringlen = len;
45669ec7b004SRick Macklem 		cp3 += len;
45679ec7b004SRick Macklem 		siz += len;
45689ec7b004SRick Macklem 		SLIST_FOREACH_SAFE(lsp, &head, next, nlsp) {
45699ec7b004SRick Macklem 			nfsrv_refstrbigenough(siz + lsp->len + stringlen + 3,
45709ec7b004SRick Macklem 			    &cp2, &cp3, &slen);
45719ec7b004SRick Macklem 			*cp3++ = ',';
45729ec7b004SRick Macklem 			NFSBCOPY(lsp->host, cp3, lsp->len);
45739ec7b004SRick Macklem 			cp3 += lsp->len;
45749ec7b004SRick Macklem 			*cp3++ = ':';
45759ec7b004SRick Macklem 			NFSBCOPY(str, cp3, stringlen);
45769ec7b004SRick Macklem 			cp3 += stringlen;
45779ec7b004SRick Macklem 			*cp3 = '\0';
45789ec7b004SRick Macklem 			siz += (lsp->len + stringlen + 2);
4579222daa42SConrad Meyer 			free(lsp, M_TEMP);
45809ec7b004SRick Macklem 		}
45819ec7b004SRick Macklem 	}
45829ec7b004SRick Macklem 	*fsrootp = cp;
45839ec7b004SRick Macklem 	*srvp = cp2;
45849ec7b004SRick Macklem 	*sump = xdrsum;
4585a9285ae5SZack Kirsch 	NFSEXITCODE2(0, nd);
45869ec7b004SRick Macklem 	return (0);
45879ec7b004SRick Macklem nfsmout:
45889ec7b004SRick Macklem 	if (cp != NULL)
45899ec7b004SRick Macklem 		free(cp, M_NFSSTRING);
45909ec7b004SRick Macklem 	if (cp2 != NULL)
45919ec7b004SRick Macklem 		free(cp2, M_NFSSTRING);
4592a9285ae5SZack Kirsch 	NFSEXITCODE2(error, nd);
45939ec7b004SRick Macklem 	return (error);
45949ec7b004SRick Macklem }
45959ec7b004SRick Macklem 
45969ec7b004SRick Macklem /*
45979ec7b004SRick Macklem  * Make the malloc'd space large enough. This is a pain, but the xdr
45989ec7b004SRick Macklem  * doesn't set an upper bound on the side, so...
45999ec7b004SRick Macklem  */
46009ec7b004SRick Macklem static void
nfsrv_refstrbigenough(int siz,u_char ** cpp,u_char ** cpp2,int * slenp)46019ec7b004SRick Macklem nfsrv_refstrbigenough(int siz, u_char **cpp, u_char **cpp2, int *slenp)
46029ec7b004SRick Macklem {
46039ec7b004SRick Macklem 	u_char *cp;
46049ec7b004SRick Macklem 	int i;
46059ec7b004SRick Macklem 
46069ec7b004SRick Macklem 	if (siz <= *slenp)
46079ec7b004SRick Macklem 		return;
46089ec7b004SRick Macklem 	cp = malloc(siz + 1024, M_NFSSTRING, M_WAITOK);
46099ec7b004SRick Macklem 	NFSBCOPY(*cpp, cp, *slenp);
46109ec7b004SRick Macklem 	free(*cpp, M_NFSSTRING);
46119ec7b004SRick Macklem 	i = *cpp2 - *cpp;
46129ec7b004SRick Macklem 	*cpp = cp;
46139ec7b004SRick Macklem 	*cpp2 = cp + i;
46149ec7b004SRick Macklem 	*slenp = siz + 1024;
46159ec7b004SRick Macklem }
46169ec7b004SRick Macklem 
46179ec7b004SRick Macklem /*
46189ec7b004SRick Macklem  * Initialize the reply header data structures.
46199ec7b004SRick Macklem  */
4620b9cc3262SRyan Moeller void
nfsrvd_rephead(struct nfsrv_descript * nd)46219ec7b004SRick Macklem nfsrvd_rephead(struct nfsrv_descript *nd)
46229ec7b004SRick Macklem {
4623ae070589SRick Macklem 	struct mbuf *mreq;
46249ec7b004SRick Macklem 
4625022346faSRick Macklem 	if ((nd->nd_flag & ND_EXTPG) != 0) {
4626022346faSRick Macklem 		mreq = mb_alloc_ext_plus_pages(PAGE_SIZE, M_WAITOK);
4627022346faSRick Macklem 		nd->nd_mreq = nd->nd_mb = mreq;
4628022346faSRick Macklem 		nd->nd_bpos = (char *)(void *)
4629022346faSRick Macklem 		    PHYS_TO_DMAP(mreq->m_epg_pa[0]);
4630022346faSRick Macklem 		nd->nd_bextpg = 0;
4631022346faSRick Macklem 		nd->nd_bextpgsiz = PAGE_SIZE;
4632022346faSRick Macklem 	} else {
46339ec7b004SRick Macklem 		/*
46349ec7b004SRick Macklem 		 * If this is a big reply, use a cluster.
46359ec7b004SRick Macklem 		 */
46369ec7b004SRick Macklem 		if ((nd->nd_flag & ND_GSSINITREPLY) == 0 &&
46379ec7b004SRick Macklem 		    nfs_bigreply[nd->nd_procnum]) {
4638eb1b1807SGleb Smirnoff 			NFSMCLGET(mreq, M_WAITOK);
46399ec7b004SRick Macklem 			nd->nd_mreq = mreq;
46409ec7b004SRick Macklem 			nd->nd_mb = mreq;
46419ec7b004SRick Macklem 		} else {
46429ec7b004SRick Macklem 			NFSMGET(mreq);
46439ec7b004SRick Macklem 			nd->nd_mreq = mreq;
46449ec7b004SRick Macklem 			nd->nd_mb = mreq;
46459ec7b004SRick Macklem 		}
4646022346faSRick Macklem 		nd->nd_bpos = mtod(mreq, char *);
4647c948a17aSRick Macklem 		mreq->m_len = 0;
4648022346faSRick Macklem 	}
46499ec7b004SRick Macklem 
46509ec7b004SRick Macklem 	if ((nd->nd_flag & ND_GSSINITREPLY) == 0)
46519ec7b004SRick Macklem 		NFSM_BUILD(nd->nd_errp, int *, NFSX_UNSIGNED);
46529ec7b004SRick Macklem }
46539ec7b004SRick Macklem 
46549ec7b004SRick Macklem /*
46559ec7b004SRick Macklem  * Lock a socket against others.
46569ec7b004SRick Macklem  * Currently used to serialize connect/disconnect attempts.
46579ec7b004SRick Macklem  */
46589ec7b004SRick Macklem int
newnfs_sndlock(int * flagp)46599ec7b004SRick Macklem newnfs_sndlock(int *flagp)
46609ec7b004SRick Macklem {
46619ec7b004SRick Macklem 	struct timespec ts;
46629ec7b004SRick Macklem 
46639ec7b004SRick Macklem 	NFSLOCKSOCK();
46649ec7b004SRick Macklem 	while (*flagp & NFSR_SNDLOCK) {
46659ec7b004SRick Macklem 		*flagp |= NFSR_WANTSND;
46669ec7b004SRick Macklem 		ts.tv_sec = 0;
46679ec7b004SRick Macklem 		ts.tv_nsec = 0;
46689ec7b004SRick Macklem 		(void) nfsmsleep((caddr_t)flagp, NFSSOCKMUTEXPTR,
46699ec7b004SRick Macklem 		    PZERO - 1, "nfsndlck", &ts);
46709ec7b004SRick Macklem 	}
46719ec7b004SRick Macklem 	*flagp |= NFSR_SNDLOCK;
46729ec7b004SRick Macklem 	NFSUNLOCKSOCK();
46739ec7b004SRick Macklem 	return (0);
46749ec7b004SRick Macklem }
46759ec7b004SRick Macklem 
46769ec7b004SRick Macklem /*
46779ec7b004SRick Macklem  * Unlock the stream socket for others.
46789ec7b004SRick Macklem  */
46799ec7b004SRick Macklem void
newnfs_sndunlock(int * flagp)46809ec7b004SRick Macklem newnfs_sndunlock(int *flagp)
46819ec7b004SRick Macklem {
46829ec7b004SRick Macklem 
46839ec7b004SRick Macklem 	NFSLOCKSOCK();
46849ec7b004SRick Macklem 	if ((*flagp & NFSR_SNDLOCK) == 0)
46859ec7b004SRick Macklem 		panic("nfs sndunlock");
46869ec7b004SRick Macklem 	*flagp &= ~NFSR_SNDLOCK;
46879ec7b004SRick Macklem 	if (*flagp & NFSR_WANTSND) {
46889ec7b004SRick Macklem 		*flagp &= ~NFSR_WANTSND;
46899ec7b004SRick Macklem 		wakeup((caddr_t)flagp);
46909ec7b004SRick Macklem 	}
46919ec7b004SRick Macklem 	NFSUNLOCKSOCK();
46929ec7b004SRick Macklem }
46939ec7b004SRick Macklem 
4694b9cc3262SRyan Moeller int
nfsv4_getipaddr(struct nfsrv_descript * nd,struct sockaddr_in * sin,struct sockaddr_in6 * sin6,sa_family_t * saf,int * isudp)4695be3d32adSRick Macklem nfsv4_getipaddr(struct nfsrv_descript *nd, struct sockaddr_in *sin,
4696be3d32adSRick Macklem     struct sockaddr_in6 *sin6, sa_family_t *saf, int *isudp)
46971f60bfd8SRick Macklem {
46981f60bfd8SRick Macklem 	struct in_addr saddr;
46991f60bfd8SRick Macklem 	uint32_t portnum, *tl;
4700be3d32adSRick Macklem 	int i, j, k;
4701be3d32adSRick Macklem 	sa_family_t af = AF_UNSPEC;
47021f60bfd8SRick Macklem 	char addr[64], protocol[5], *cp;
47031f60bfd8SRick Macklem 	int cantparse = 0, error = 0;
47041f60bfd8SRick Macklem 	uint16_t portv;
47051f60bfd8SRick Macklem 
47061f60bfd8SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
47071f60bfd8SRick Macklem 	i = fxdr_unsigned(int, *tl);
47081f60bfd8SRick Macklem 	if (i >= 3 && i <= 4) {
47091f60bfd8SRick Macklem 		error = nfsrv_mtostr(nd, protocol, i);
47101f60bfd8SRick Macklem 		if (error)
47111f60bfd8SRick Macklem 			goto nfsmout;
47121f60bfd8SRick Macklem 		if (strcmp(protocol, "tcp") == 0) {
47131f60bfd8SRick Macklem 			af = AF_INET;
47141f60bfd8SRick Macklem 			*isudp = 0;
47151f60bfd8SRick Macklem 		} else if (strcmp(protocol, "udp") == 0) {
47161f60bfd8SRick Macklem 			af = AF_INET;
47171f60bfd8SRick Macklem 			*isudp = 1;
47181f60bfd8SRick Macklem 		} else if (strcmp(protocol, "tcp6") == 0) {
47191f60bfd8SRick Macklem 			af = AF_INET6;
47201f60bfd8SRick Macklem 			*isudp = 0;
47211f60bfd8SRick Macklem 		} else if (strcmp(protocol, "udp6") == 0) {
47221f60bfd8SRick Macklem 			af = AF_INET6;
47231f60bfd8SRick Macklem 			*isudp = 1;
47241f60bfd8SRick Macklem 		} else
47251f60bfd8SRick Macklem 			cantparse = 1;
47261f60bfd8SRick Macklem 	} else {
47271f60bfd8SRick Macklem 		cantparse = 1;
47281f60bfd8SRick Macklem 		if (i > 0) {
47291f60bfd8SRick Macklem 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
47301f60bfd8SRick Macklem 			if (error)
47311f60bfd8SRick Macklem 				goto nfsmout;
47321f60bfd8SRick Macklem 		}
47331f60bfd8SRick Macklem 	}
47341f60bfd8SRick Macklem 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
47351f60bfd8SRick Macklem 	i = fxdr_unsigned(int, *tl);
47361f60bfd8SRick Macklem 	if (i < 0) {
47371f60bfd8SRick Macklem 		error = NFSERR_BADXDR;
47381f60bfd8SRick Macklem 		goto nfsmout;
47391f60bfd8SRick Macklem 	} else if (cantparse == 0 && i >= 11 && i < 64) {
47401f60bfd8SRick Macklem 		/*
47411f60bfd8SRick Macklem 		 * The shortest address is 11chars and the longest is < 64.
47421f60bfd8SRick Macklem 		 */
47431f60bfd8SRick Macklem 		error = nfsrv_mtostr(nd, addr, i);
47441f60bfd8SRick Macklem 		if (error)
47451f60bfd8SRick Macklem 			goto nfsmout;
47461f60bfd8SRick Macklem 
47471f60bfd8SRick Macklem 		/* Find the port# at the end and extract that. */
47481f60bfd8SRick Macklem 		i = strlen(addr);
47491f60bfd8SRick Macklem 		k = 0;
47501f60bfd8SRick Macklem 		cp = &addr[i - 1];
47511f60bfd8SRick Macklem 		/* Count back two '.'s from end to get port# field. */
47521f60bfd8SRick Macklem 		for (j = 0; j < i; j++) {
47531f60bfd8SRick Macklem 			if (*cp == '.') {
47541f60bfd8SRick Macklem 				k++;
47551f60bfd8SRick Macklem 				if (k == 2)
47561f60bfd8SRick Macklem 					break;
47571f60bfd8SRick Macklem 			}
47581f60bfd8SRick Macklem 			cp--;
47591f60bfd8SRick Macklem 		}
47601f60bfd8SRick Macklem 		if (k == 2) {
47611f60bfd8SRick Macklem 			/*
47621f60bfd8SRick Macklem 			 * The NFSv4 port# is appended as .N.N, where N is
47631f60bfd8SRick Macklem 			 * a decimal # in the range 0-255, just like an inet4
47641f60bfd8SRick Macklem 			 * address. Cheat and use inet_aton(), which will
47651f60bfd8SRick Macklem 			 * return a Class A address and then shift the high
47661f60bfd8SRick Macklem 			 * order 8bits over to convert it to the port#.
47671f60bfd8SRick Macklem 			 */
47681f60bfd8SRick Macklem 			*cp++ = '\0';
47691f60bfd8SRick Macklem 			if (inet_aton(cp, &saddr) == 1) {
47701f60bfd8SRick Macklem 				portnum = ntohl(saddr.s_addr);
47711f60bfd8SRick Macklem 				portv = (uint16_t)((portnum >> 16) |
47721f60bfd8SRick Macklem 				    (portnum & 0xff));
47731f60bfd8SRick Macklem 			} else
47741f60bfd8SRick Macklem 				cantparse = 1;
47751f60bfd8SRick Macklem 		} else
47761f60bfd8SRick Macklem 			cantparse = 1;
47771f60bfd8SRick Macklem 		if (cantparse == 0) {
47781f60bfd8SRick Macklem 			if (af == AF_INET) {
4779be3d32adSRick Macklem 				if (inet_pton(af, addr, &sin->sin_addr) == 1) {
4780be3d32adSRick Macklem 					sin->sin_len = sizeof(*sin);
4781be3d32adSRick Macklem 					sin->sin_family = AF_INET;
4782be3d32adSRick Macklem 					sin->sin_port = htons(portv);
4783be3d32adSRick Macklem 					*saf = af;
47841f60bfd8SRick Macklem 					return (0);
47851f60bfd8SRick Macklem 				}
47861f60bfd8SRick Macklem 			} else {
4787be3d32adSRick Macklem 				if (inet_pton(af, addr, &sin6->sin6_addr)
47881f60bfd8SRick Macklem 				    == 1) {
4789be3d32adSRick Macklem 					sin6->sin6_len = sizeof(*sin6);
4790be3d32adSRick Macklem 					sin6->sin6_family = AF_INET6;
4791be3d32adSRick Macklem 					sin6->sin6_port = htons(portv);
4792be3d32adSRick Macklem 					*saf = af;
47931f60bfd8SRick Macklem 					return (0);
47941f60bfd8SRick Macklem 				}
47951f60bfd8SRick Macklem 			}
47961f60bfd8SRick Macklem 		}
47971f60bfd8SRick Macklem 	} else {
47981f60bfd8SRick Macklem 		if (i > 0) {
47991f60bfd8SRick Macklem 			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
48001f60bfd8SRick Macklem 			if (error)
48011f60bfd8SRick Macklem 				goto nfsmout;
48021f60bfd8SRick Macklem 		}
48031f60bfd8SRick Macklem 	}
48041f60bfd8SRick Macklem 	error = EPERM;
48051f60bfd8SRick Macklem nfsmout:
48061f60bfd8SRick Macklem 	return (error);
48071f60bfd8SRick Macklem }
48081f60bfd8SRick Macklem 
48091f60bfd8SRick Macklem /*
48101f60bfd8SRick Macklem  * Handle an NFSv4.1 Sequence request for the session.
4811c59e4cc3SRick Macklem  * If reply != NULL, use it to return the cached reply, as required.
4812c59e4cc3SRick Macklem  * The client gets a cached reply via this call for callbacks, however the
481378ffcb86SRick Macklem  * server gets a cached reply via the nfsv4_seqsess_cacherep() call.
48141f60bfd8SRick Macklem  */
48151f60bfd8SRick Macklem int
nfsv4_seqsession(uint32_t seqid,uint32_t slotid,uint32_t highslot,struct nfsslot * slots,struct mbuf ** reply,uint16_t maxslot)48161f60bfd8SRick Macklem nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot,
48171f60bfd8SRick Macklem     struct nfsslot *slots, struct mbuf **reply, uint16_t maxslot)
48181f60bfd8SRick Macklem {
481922cefe3dSRick Macklem 	struct mbuf *m;
48201f60bfd8SRick Macklem 	int error;
48211f60bfd8SRick Macklem 
48221f60bfd8SRick Macklem 	error = 0;
4823c59e4cc3SRick Macklem 	if (reply != NULL)
48241f60bfd8SRick Macklem 		*reply = NULL;
48251f60bfd8SRick Macklem 	if (slotid > maxslot)
48261f60bfd8SRick Macklem 		return (NFSERR_BADSLOT);
48271f60bfd8SRick Macklem 	if (seqid == slots[slotid].nfssl_seq) {
48281f60bfd8SRick Macklem 		/* A retry. */
48291f60bfd8SRick Macklem 		if (slots[slotid].nfssl_inprog != 0)
48301f60bfd8SRick Macklem 			error = NFSERR_DELAY;
48311f60bfd8SRick Macklem 		else if (slots[slotid].nfssl_reply != NULL) {
4832c59e4cc3SRick Macklem 			if (reply != NULL) {
483322cefe3dSRick Macklem 				m = m_copym(slots[slotid].nfssl_reply, 0,
483422cefe3dSRick Macklem 				    M_COPYALL, M_NOWAIT);
483522cefe3dSRick Macklem 				if (m != NULL)
483622cefe3dSRick Macklem 					*reply = m;
483722cefe3dSRick Macklem 				else {
48381f60bfd8SRick Macklem 					*reply = slots[slotid].nfssl_reply;
48391f60bfd8SRick Macklem 					slots[slotid].nfssl_reply = NULL;
4840c59e4cc3SRick Macklem 				}
484122cefe3dSRick Macklem 			}
48421f60bfd8SRick Macklem 			slots[slotid].nfssl_inprog = 1;
4843c59e4cc3SRick Macklem 			error = NFSERR_REPLYFROMCACHE;
48441f60bfd8SRick Macklem 		} else
4845c59e4cc3SRick Macklem 			/* No reply cached, so just do it. */
4846c59e4cc3SRick Macklem 			slots[slotid].nfssl_inprog = 1;
484734256484SRick Macklem 	} else if ((slots[slotid].nfssl_seq + 1) == seqid) {
4848c59e4cc3SRick Macklem 		if (slots[slotid].nfssl_reply != NULL)
48491f60bfd8SRick Macklem 			m_freem(slots[slotid].nfssl_reply);
48501f60bfd8SRick Macklem 		slots[slotid].nfssl_reply = NULL;
48511f60bfd8SRick Macklem 		slots[slotid].nfssl_inprog = 1;
485234256484SRick Macklem 		slots[slotid].nfssl_seq++;
48531f60bfd8SRick Macklem 	} else
48541f60bfd8SRick Macklem 		error = NFSERR_SEQMISORDERED;
48551f60bfd8SRick Macklem 	return (error);
48561f60bfd8SRick Macklem }
48571f60bfd8SRick Macklem 
48581f60bfd8SRick Macklem /*
48591f60bfd8SRick Macklem  * Cache this reply for the slot.
4860c59e4cc3SRick Macklem  * Use the "rep" argument to return the cached reply if repstat is set to
4861c59e4cc3SRick Macklem  * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value.
48621f60bfd8SRick Macklem  */
48631f60bfd8SRick Macklem void
nfsv4_seqsess_cacherep(uint32_t slotid,struct nfsslot * slots,int repstat,struct mbuf ** rep)4864c59e4cc3SRick Macklem nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat,
4865c59e4cc3SRick Macklem    struct mbuf **rep)
48661f60bfd8SRick Macklem {
486722cefe3dSRick Macklem 	struct mbuf *m;
48681f60bfd8SRick Macklem 
4869c59e4cc3SRick Macklem 	if (repstat == NFSERR_REPLYFROMCACHE) {
487022cefe3dSRick Macklem 		if (slots[slotid].nfssl_reply != NULL) {
487122cefe3dSRick Macklem 			/*
487222cefe3dSRick Macklem 			 * We cannot sleep here, but copy will usually
487322cefe3dSRick Macklem 			 * succeed.
487422cefe3dSRick Macklem 			 */
487522cefe3dSRick Macklem 			m = m_copym(slots[slotid].nfssl_reply, 0, M_COPYALL,
487622cefe3dSRick Macklem 			    M_NOWAIT);
487722cefe3dSRick Macklem 			if (m != NULL)
487822cefe3dSRick Macklem 				*rep = m;
487922cefe3dSRick Macklem 			else {
488022cefe3dSRick Macklem 				/*
488122cefe3dSRick Macklem 				 * Multiple retries would be extremely rare,
488222cefe3dSRick Macklem 				 * so using the cached reply will likely
488322cefe3dSRick Macklem 				 * be ok.
488422cefe3dSRick Macklem 				 */
4885c59e4cc3SRick Macklem 				*rep = slots[slotid].nfssl_reply;
4886c59e4cc3SRick Macklem 				slots[slotid].nfssl_reply = NULL;
488722cefe3dSRick Macklem 			}
488822cefe3dSRick Macklem 		} else
488922cefe3dSRick Macklem 			*rep = NULL;
4890c59e4cc3SRick Macklem 	} else {
4891c59e4cc3SRick Macklem 		if (slots[slotid].nfssl_reply != NULL)
4892c59e4cc3SRick Macklem 			m_freem(slots[slotid].nfssl_reply);
4893c59e4cc3SRick Macklem 		slots[slotid].nfssl_reply = *rep;
4894c59e4cc3SRick Macklem 	}
48951f60bfd8SRick Macklem 	slots[slotid].nfssl_inprog = 0;
48961f60bfd8SRick Macklem }
48971f60bfd8SRick Macklem 
48981f60bfd8SRick Macklem /*
48991f60bfd8SRick Macklem  * Generate the xdr for an NFSv4.1 Sequence Operation.
49001f60bfd8SRick Macklem  */
4901b9cc3262SRyan Moeller void
nfsv4_setsequence(struct nfsmount * nmp,struct nfsrv_descript * nd,struct nfsclsession * sep,int dont_replycache,struct ucred * cred)490242b6336aSRick Macklem nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
490340ada74eSRick Macklem     struct nfsclsession *sep, int dont_replycache, struct ucred *cred)
49041f60bfd8SRick Macklem {
49051f60bfd8SRick Macklem 	uint32_t *tl, slotseq = 0;
4906c59e4cc3SRick Macklem 	int error, maxslot, slotpos;
49071f60bfd8SRick Macklem 	uint8_t sessionid[NFSX_V4SESSIONID];
49081f60bfd8SRick Macklem 
490940ada74eSRick Macklem 	if (cred != NULL) {
491040ada74eSRick Macklem 		error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot,
491140ada74eSRick Macklem 		    &slotseq, sessionid, false);
491240ada74eSRick Macklem 		if (error == NFSERR_SEQMISORDERED) {
491340ada74eSRick Macklem 			/* If all slots are bad, Destroy the session. */
491440ada74eSRick Macklem 			nfsrpc_destroysession(nmp, sep, cred, curthread);
491540ada74eSRick Macklem 		}
491640ada74eSRick Macklem 	} else
491740ada74eSRick Macklem 		error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot,
491840ada74eSRick Macklem 		    &slotseq, sessionid, true);
4919c057a378SRick Macklem 	nd->nd_maxreq = sep->nfsess_maxreq;
4920c057a378SRick Macklem 	nd->nd_maxresp = sep->nfsess_maxresp;
49211f60bfd8SRick Macklem 
49221f60bfd8SRick Macklem 	/* Build the Sequence arguments. */
49231f60bfd8SRick Macklem 	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
4924b2fc0141SRick Macklem 	nd->nd_sequence = tl;
49251f60bfd8SRick Macklem 	bcopy(sessionid, tl, NFSX_V4SESSIONID);
49261f60bfd8SRick Macklem 	tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
49271f60bfd8SRick Macklem 	nd->nd_slotseq = tl;
4928b2fc0141SRick Macklem 	if (error == 0) {
49299f4c522eSRick Macklem 		nd->nd_flag |= ND_HASSLOTID;
49309f4c522eSRick Macklem 		nd->nd_slotid = slotpos;
49311f60bfd8SRick Macklem 		*tl++ = txdr_unsigned(slotseq);
49321f60bfd8SRick Macklem 		*tl++ = txdr_unsigned(slotpos);
49331f60bfd8SRick Macklem 		*tl++ = txdr_unsigned(maxslot);
49341f60bfd8SRick Macklem 		if (dont_replycache == 0)
49351f60bfd8SRick Macklem 			*tl = newnfs_true;
49361f60bfd8SRick Macklem 		else
49371f60bfd8SRick Macklem 			*tl = newnfs_false;
4938b2fc0141SRick Macklem 	} else {
4939b2fc0141SRick Macklem 		/*
4940b2fc0141SRick Macklem 		 * There are two errors and the rest of the session can
4941b2fc0141SRick Macklem 		 * just be zeros.
4942b2fc0141SRick Macklem 		 * NFSERR_BADSESSION: This bad session should just generate
4943b2fc0141SRick Macklem 		 *    the same error again when the RPC is retried.
4944b2fc0141SRick Macklem 		 * ESTALE: A forced dismount is in progress and will cause the
4945b2fc0141SRick Macklem 		 *    RPC to fail later.
4946b2fc0141SRick Macklem 		 */
4947b2fc0141SRick Macklem 		*tl++ = 0;
4948b2fc0141SRick Macklem 		*tl++ = 0;
4949b2fc0141SRick Macklem 		*tl++ = 0;
4950b2fc0141SRick Macklem 		*tl = 0;
4951b2fc0141SRick Macklem 	}
49521f60bfd8SRick Macklem 	nd->nd_flag |= ND_HASSEQUENCE;
49531f60bfd8SRick Macklem }
49541f60bfd8SRick Macklem 
495540ada74eSRick Macklem /*
495640ada74eSRick Macklem  * If fnd_init is true, ignore the badslots.
495740ada74eSRick Macklem  * If fnd_init is false, return NFSERR_SEQMISORDERED if all slots are bad.
495840ada74eSRick Macklem  */
4959c59e4cc3SRick Macklem int
nfsv4_sequencelookup(struct nfsmount * nmp,struct nfsclsession * sep,int * slotposp,int * maxslotp,uint32_t * slotseqp,uint8_t * sessionid,bool fnd_init)4960c59e4cc3SRick Macklem nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
496140ada74eSRick Macklem     int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid,
496240ada74eSRick Macklem     bool fnd_init)
4963c59e4cc3SRick Macklem {
4964c59e4cc3SRick Macklem 	int i, maxslot, slotpos;
4965c59e4cc3SRick Macklem 	uint64_t bitval;
496640ada74eSRick Macklem 	bool fnd_ok;
4967c59e4cc3SRick Macklem 
4968c59e4cc3SRick Macklem 	/* Find an unused slot. */
4969c59e4cc3SRick Macklem 	slotpos = -1;
4970c59e4cc3SRick Macklem 	maxslot = -1;
4971c59e4cc3SRick Macklem 	mtx_lock(&sep->nfsess_mtx);
4972c59e4cc3SRick Macklem 	do {
4973b2fc0141SRick Macklem 		if (nmp != NULL && sep->nfsess_defunct != 0) {
4974b2fc0141SRick Macklem 			/* Just return the bad session. */
4975b2fc0141SRick Macklem 			bcopy(sep->nfsess_sessionid, sessionid,
4976b2fc0141SRick Macklem 			    NFSX_V4SESSIONID);
4977b2fc0141SRick Macklem 			mtx_unlock(&sep->nfsess_mtx);
4978b2fc0141SRick Macklem 			return (NFSERR_BADSESSION);
4979b2fc0141SRick Macklem 		}
498040ada74eSRick Macklem 		fnd_ok = fnd_init;
4981c59e4cc3SRick Macklem 		bitval = 1;
4982c59e4cc3SRick Macklem 		for (i = 0; i < sep->nfsess_foreslots; i++) {
498340ada74eSRick Macklem 			if ((bitval & sep->nfsess_badslots) == 0 || fnd_init) {
498440ada74eSRick Macklem 				fnd_ok = true;
4985c59e4cc3SRick Macklem 				if ((bitval & sep->nfsess_slots) == 0) {
4986c59e4cc3SRick Macklem 					slotpos = i;
4987c59e4cc3SRick Macklem 					sep->nfsess_slots |= bitval;
4988c59e4cc3SRick Macklem 					sep->nfsess_slotseq[i]++;
4989c59e4cc3SRick Macklem 					*slotseqp = sep->nfsess_slotseq[i];
4990c59e4cc3SRick Macklem 					break;
4991c59e4cc3SRick Macklem 				}
499240ada74eSRick Macklem 			}
4993c59e4cc3SRick Macklem 			bitval <<= 1;
4994c59e4cc3SRick Macklem 		}
4995c59e4cc3SRick Macklem 		if (slotpos == -1) {
4996c59e4cc3SRick Macklem 			/*
4997c59e4cc3SRick Macklem 			 * If a forced dismount is in progress, just return.
4998c59e4cc3SRick Macklem 			 * This RPC attempt will fail when it calls
4999c59e4cc3SRick Macklem 			 * newnfs_request().
5000c59e4cc3SRick Macklem 			 */
500116f300faSRick Macklem 			if (nmp != NULL && NFSCL_FORCEDISM(nmp->nm_mountp)) {
5002c59e4cc3SRick Macklem 				mtx_unlock(&sep->nfsess_mtx);
5003c59e4cc3SRick Macklem 				return (ESTALE);
5004c59e4cc3SRick Macklem 			}
5005c59e4cc3SRick Macklem 			/* Wake up once/sec, to check for a forced dismount. */
500640ada74eSRick Macklem 			if (fnd_ok)
500740ada74eSRick Macklem 				mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx,
5008c59e4cc3SRick Macklem 				    PZERO, "nfsclseq", hz);
5009c59e4cc3SRick Macklem 		}
501040ada74eSRick Macklem 	} while (slotpos == -1 && fnd_ok);
501140ada74eSRick Macklem 	/*
501240ada74eSRick Macklem 	 * If all slots are bad, just return slot 0 and NFSERR_SEQMISORDERED.
501340ada74eSRick Macklem 	 * The caller will do a DestroySession, so that the session's use
501440ada74eSRick Macklem 	 * will get a NFSERR_BADSESSION reply from the server.
501540ada74eSRick Macklem 	 */
501640ada74eSRick Macklem 	if (!fnd_ok)
501740ada74eSRick Macklem 		slotpos = 0;
501840ada74eSRick Macklem 
5019c59e4cc3SRick Macklem 	/* Now, find the highest slot in use. (nfsc_slots is 64bits) */
5020c59e4cc3SRick Macklem 	bitval = 1;
5021c59e4cc3SRick Macklem 	for (i = 0; i < 64; i++) {
5022c59e4cc3SRick Macklem 		if ((bitval & sep->nfsess_slots) != 0)
5023c59e4cc3SRick Macklem 			maxslot = i;
5024c59e4cc3SRick Macklem 		bitval <<= 1;
5025c59e4cc3SRick Macklem 	}
5026c59e4cc3SRick Macklem 	bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID);
5027c59e4cc3SRick Macklem 	mtx_unlock(&sep->nfsess_mtx);
5028c59e4cc3SRick Macklem 	*slotposp = slotpos;
5029c59e4cc3SRick Macklem 	*maxslotp = maxslot;
503040ada74eSRick Macklem 
503140ada74eSRick Macklem 	if (!fnd_ok)
503240ada74eSRick Macklem 		return (NFSERR_SEQMISORDERED);
5033c59e4cc3SRick Macklem 	return (0);
5034c59e4cc3SRick Macklem }
5035c59e4cc3SRick Macklem 
50361f60bfd8SRick Macklem /*
50371f60bfd8SRick Macklem  * Free a session slot.
50381f60bfd8SRick Macklem  */
5039b9cc3262SRyan Moeller void
nfsv4_freeslot(struct nfsclsession * sep,int slot,bool resetseq)504087597731SRick Macklem nfsv4_freeslot(struct nfsclsession *sep, int slot, bool resetseq)
50411f60bfd8SRick Macklem {
50421f60bfd8SRick Macklem 	uint64_t bitval;
50431f60bfd8SRick Macklem 
50441f60bfd8SRick Macklem 	bitval = 1;
50451f60bfd8SRick Macklem 	if (slot > 0)
50461f60bfd8SRick Macklem 		bitval <<= slot;
50471f60bfd8SRick Macklem 	mtx_lock(&sep->nfsess_mtx);
504887597731SRick Macklem 	if (resetseq)
504987597731SRick Macklem 		sep->nfsess_slotseq[slot]--;
5050*b97a4788SRick Macklem 	else if (slot > sep->nfsess_foreslots)
5051*b97a4788SRick Macklem 		sep->nfsess_slotseq[slot] = 0;
50521f60bfd8SRick Macklem 	if ((bitval & sep->nfsess_slots) == 0)
50531f60bfd8SRick Macklem 		printf("freeing free slot!!\n");
50541f60bfd8SRick Macklem 	sep->nfsess_slots &= ~bitval;
50551f60bfd8SRick Macklem 	wakeup(&sep->nfsess_slots);
50561f60bfd8SRick Macklem 	mtx_unlock(&sep->nfsess_mtx);
50571f60bfd8SRick Macklem }
50581f60bfd8SRick Macklem 
505990d2dfabSRick Macklem /*
50602f32675cSRick Macklem  * Search for a matching pnfsd DS, based on the nmp arg.
506190d2dfabSRick Macklem  * Return one if found, NULL otherwise.
506290d2dfabSRick Macklem  */
506390d2dfabSRick Macklem struct nfsdevice *
nfsv4_findmirror(struct nfsmount * nmp)506490d2dfabSRick Macklem nfsv4_findmirror(struct nfsmount *nmp)
506590d2dfabSRick Macklem {
50662f32675cSRick Macklem 	struct nfsdevice *ds;
506790d2dfabSRick Macklem 
506890d2dfabSRick Macklem 	mtx_assert(NFSDDSMUTEXPTR, MA_OWNED);
506990d2dfabSRick Macklem 	/*
507090d2dfabSRick Macklem 	 * Search the DS server list for a match with nmp.
507190d2dfabSRick Macklem 	 */
507290d2dfabSRick Macklem 	if (nfsrv_devidcnt == 0)
50732f32675cSRick Macklem 		return (NULL);
507490d2dfabSRick Macklem 	TAILQ_FOREACH(ds, &nfsrv_devidhead, nfsdev_list) {
507590d2dfabSRick Macklem 		if (ds->nfsdev_nmp == nmp) {
50762f32675cSRick Macklem 			NFSCL_DEBUG(4, "nfsv4_findmirror: fnd main ds\n");
507790d2dfabSRick Macklem 			break;
507890d2dfabSRick Macklem 		}
507990d2dfabSRick Macklem 	}
50802f32675cSRick Macklem 	return (ds);
508190d2dfabSRick Macklem }
508290d2dfabSRick Macklem 
50833d7650f0SRick Macklem /*
50843d7650f0SRick Macklem  * Fill in the fields of "struct nfsrv_descript".
50853d7650f0SRick Macklem  */
50863d7650f0SRick Macklem void
nfsm_set(struct nfsrv_descript * nd,u_int offs)50873d7650f0SRick Macklem nfsm_set(struct nfsrv_descript *nd, u_int offs)
50883d7650f0SRick Macklem {
50893d7650f0SRick Macklem 	struct mbuf *m;
5090dccb5806SRick Macklem 	int rlen;
50913d7650f0SRick Macklem 
50923d7650f0SRick Macklem 	m = nd->nd_mb;
5093dccb5806SRick Macklem 	if ((m->m_flags & M_EXTPG) != 0) {
5094dccb5806SRick Macklem 		nd->nd_bextpg = 0;
5095dccb5806SRick Macklem 		while (offs > 0) {
5096dccb5806SRick Macklem 			if (nd->nd_bextpg == 0)
5097dccb5806SRick Macklem 				rlen = m_epg_pagelen(m, 0, m->m_epg_1st_off);
5098dccb5806SRick Macklem 			else
5099dccb5806SRick Macklem 				rlen = m_epg_pagelen(m, nd->nd_bextpg, 0);
5100dccb5806SRick Macklem 			if (offs <= rlen)
5101dccb5806SRick Macklem 				break;
5102dccb5806SRick Macklem 			offs -= rlen;
5103dccb5806SRick Macklem 			nd->nd_bextpg++;
5104dccb5806SRick Macklem 			if (nd->nd_bextpg == m->m_epg_npgs) {
5105dccb5806SRick Macklem 				printf("nfsm_set: build offs "
5106dccb5806SRick Macklem 				    "out of range\n");
5107dccb5806SRick Macklem 				nd->nd_bextpg--;
5108dccb5806SRick Macklem 				break;
5109dccb5806SRick Macklem 			}
5110dccb5806SRick Macklem 		}
5111dccb5806SRick Macklem 		nd->nd_bpos = (char *)(void *)
5112dccb5806SRick Macklem 		    PHYS_TO_DMAP(m->m_epg_pa[nd->nd_bextpg]);
5113dccb5806SRick Macklem 		if (nd->nd_bextpg == 0)
5114dccb5806SRick Macklem 			nd->nd_bpos += m->m_epg_1st_off;
5115dccb5806SRick Macklem 		if (offs > 0) {
5116dccb5806SRick Macklem 			nd->nd_bpos += offs;
5117dccb5806SRick Macklem 			nd->nd_bextpgsiz = rlen - offs;
5118dccb5806SRick Macklem 		} else if (nd->nd_bextpg == 0)
5119dccb5806SRick Macklem 			nd->nd_bextpgsiz = PAGE_SIZE - m->m_epg_1st_off;
5120dccb5806SRick Macklem 		else
5121dccb5806SRick Macklem 			nd->nd_bextpgsiz = PAGE_SIZE;
5122dccb5806SRick Macklem 	} else
51233d7650f0SRick Macklem 		nd->nd_bpos = mtod(m, char *) + offs;
51243d7650f0SRick Macklem }
512534fc29e0SRick Macklem 
512634fc29e0SRick Macklem /*
512734fc29e0SRick Macklem  * Grow a ext_pgs mbuf list.  Either allocate another page or add
512834fc29e0SRick Macklem  * an mbuf to the list.
512934fc29e0SRick Macklem  */
513034fc29e0SRick Macklem struct mbuf *
nfsm_add_ext_pgs(struct mbuf * m,int maxextsiz,int * bextpg)513134fc29e0SRick Macklem nfsm_add_ext_pgs(struct mbuf *m, int maxextsiz, int *bextpg)
513234fc29e0SRick Macklem {
513334fc29e0SRick Macklem 	struct mbuf *mp;
513434fc29e0SRick Macklem 	vm_page_t pg;
513534fc29e0SRick Macklem 
513634fc29e0SRick Macklem 	if ((m->m_epg_npgs + 1) * PAGE_SIZE > maxextsiz) {
513734fc29e0SRick Macklem 		mp = mb_alloc_ext_plus_pages(PAGE_SIZE, M_WAITOK);
513834fc29e0SRick Macklem 		*bextpg = 0;
513934fc29e0SRick Macklem 		m->m_next = mp;
514034fc29e0SRick Macklem 	} else {
5141a4667e09SMark Johnston 		pg = vm_page_alloc_noobj(VM_ALLOC_WAITOK | VM_ALLOC_NODUMP |
514234fc29e0SRick Macklem 		    VM_ALLOC_WIRED);
514334fc29e0SRick Macklem 		m->m_epg_pa[m->m_epg_npgs] = VM_PAGE_TO_PHYS(pg);
514434fc29e0SRick Macklem 		*bextpg = m->m_epg_npgs;
514534fc29e0SRick Macklem 		m->m_epg_npgs++;
514634fc29e0SRick Macklem 		m->m_epg_last_len = 0;
514734fc29e0SRick Macklem 		mp = m;
514834fc29e0SRick Macklem 	}
514934fc29e0SRick Macklem 	return (mp);
515034fc29e0SRick Macklem }
5151dff31ae1SRick Macklem 
5152dff31ae1SRick Macklem /*
5153dff31ae1SRick Macklem  * Do the NFSv4.1 Destroy Session.
5154dff31ae1SRick Macklem  */
5155dff31ae1SRick Macklem int
nfsrpc_destroysession(struct nfsmount * nmp,struct nfsclsession * tsep,struct ucred * cred,NFSPROC_T * p)5156dff31ae1SRick Macklem nfsrpc_destroysession(struct nfsmount *nmp, struct nfsclsession *tsep,
5157dff31ae1SRick Macklem     struct ucred *cred, NFSPROC_T *p)
5158dff31ae1SRick Macklem {
5159dff31ae1SRick Macklem 	uint32_t *tl;
5160dff31ae1SRick Macklem 	struct nfsrv_descript nfsd;
5161dff31ae1SRick Macklem 	struct nfsrv_descript *nd = &nfsd;
5162dff31ae1SRick Macklem 	int error;
5163dff31ae1SRick Macklem 
5164db7257efSRick Macklem 	if (tsep == NULL)
5165db7257efSRick Macklem 		tsep = nfsmnt_mdssession(nmp);
5166db7257efSRick Macklem 	if (tsep == NULL)
5167db7257efSRick Macklem 		return (0);
5168dff31ae1SRick Macklem 	nfscl_reqstart(nd, NFSPROC_DESTROYSESSION, nmp, NULL, 0, NULL, NULL, 0,
5169dff31ae1SRick Macklem 	    0, NULL);
5170dff31ae1SRick Macklem 	NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
5171dff31ae1SRick Macklem 	bcopy(tsep->nfsess_sessionid, tl, NFSX_V4SESSIONID);
5172dff31ae1SRick Macklem 	nd->nd_flag |= ND_USEGSSNAME;
5173dff31ae1SRick Macklem 	error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
5174dff31ae1SRick Macklem 	    NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
5175dff31ae1SRick Macklem 	if (error != 0)
5176dff31ae1SRick Macklem 		return (error);
5177dff31ae1SRick Macklem 	error = nd->nd_repstat;
5178dff31ae1SRick Macklem 	m_freem(nd->nd_mrep);
5179dff31ae1SRick Macklem 	return (error);
5180dff31ae1SRick Macklem }
5181