xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4_srv.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*
28*7c478bd9Sstevel@tonic-gate  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
29*7c478bd9Sstevel@tonic-gate  *	All Rights Reserved
30*7c478bd9Sstevel@tonic-gate  */
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/buf.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/uio.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/flock.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/nbmlock.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/share.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/policy.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/fem.h>
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate #include <rpc/types.h>
59*7c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
60*7c478bd9Sstevel@tonic-gate #include <rpc/rpcsec_gss.h>
61*7c478bd9Sstevel@tonic-gate #include <rpc/svc.h>
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
64*7c478bd9Sstevel@tonic-gate #include <nfs/export.h>
65*7c478bd9Sstevel@tonic-gate #include <nfs/lm.h>
66*7c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
69*7c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
70*7c478bd9Sstevel@tonic-gate 
71*7c478bd9Sstevel@tonic-gate #include <inet/common.h>
72*7c478bd9Sstevel@tonic-gate #include <inet/ip.h>
73*7c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate #define	RFS4_MAXLOCK_TRIES 4	/* Try to get the lock this many times */
76*7c478bd9Sstevel@tonic-gate static int rfs4_maxlock_tries = RFS4_MAXLOCK_TRIES;
77*7c478bd9Sstevel@tonic-gate #define	RFS4_LOCK_DELAY 10	/* Milliseconds */
78*7c478bd9Sstevel@tonic-gate static clock_t rfs4_lock_delay = RFS4_LOCK_DELAY;
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate /* End of Tunables */
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate /*
83*7c478bd9Sstevel@tonic-gate  * Used to bump the stateid4.seqid value and show changes in the stateid
84*7c478bd9Sstevel@tonic-gate  */
85*7c478bd9Sstevel@tonic-gate #define	next_stateid(sp) (++(sp)->bits.chgseq)
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate /*
88*7c478bd9Sstevel@tonic-gate  * RFS4_MINLEN_ENTRY4: XDR-encoded size of smallest possible dirent.
89*7c478bd9Sstevel@tonic-gate  *	This is used to return NFS4ERR_TOOSMALL when clients specify
90*7c478bd9Sstevel@tonic-gate  *	maxcount that isn't large enough to hold the smallest possible
91*7c478bd9Sstevel@tonic-gate  *	XDR encoded dirent.
92*7c478bd9Sstevel@tonic-gate  *
93*7c478bd9Sstevel@tonic-gate  *	    sizeof cookie (8 bytes) +
94*7c478bd9Sstevel@tonic-gate  *	    sizeof name_len (4 bytes) +
95*7c478bd9Sstevel@tonic-gate  *	    sizeof smallest (padded) name (4 bytes) +
96*7c478bd9Sstevel@tonic-gate  *	    sizeof bitmap4_len (12 bytes) +   NOTE: we always encode len=2 bm4
97*7c478bd9Sstevel@tonic-gate  *	    sizeof attrlist4_len (4 bytes) +
98*7c478bd9Sstevel@tonic-gate  *	    sizeof next boolean (4 bytes)
99*7c478bd9Sstevel@tonic-gate  *
100*7c478bd9Sstevel@tonic-gate  * RFS4_MINLEN_RDDIR4: XDR-encoded size of READDIR op reply containing
101*7c478bd9Sstevel@tonic-gate  * the smallest possible entry4 (assumes no attrs requested).
102*7c478bd9Sstevel@tonic-gate  *	sizeof nfsstat4 (4 bytes) +
103*7c478bd9Sstevel@tonic-gate  *	sizeof verifier4 (8 bytes) +
104*7c478bd9Sstevel@tonic-gate  *	sizeof entry4list bool (4 bytes) +
105*7c478bd9Sstevel@tonic-gate  *	sizeof entry4 	(36 bytes) +
106*7c478bd9Sstevel@tonic-gate  *	sizeof eof bool  (4 bytes)
107*7c478bd9Sstevel@tonic-gate  *
108*7c478bd9Sstevel@tonic-gate  * RFS4_MINLEN_RDDIR_BUF: minimum length of buffer server will provide to
109*7c478bd9Sstevel@tonic-gate  *	VOP_READDIR.  Its value is the size of the maximum possible dirent
110*7c478bd9Sstevel@tonic-gate  *	for solaris.  The DIRENT64_RECLEN macro returns	the size of dirent
111*7c478bd9Sstevel@tonic-gate  *	required for a given name length.  MAXNAMELEN is the maximum
112*7c478bd9Sstevel@tonic-gate  *	filename length allowed in Solaris.  The first two DIRENT64_RECLEN()
113*7c478bd9Sstevel@tonic-gate  *	macros are to allow for . and .. entries -- just a minor tweak to try
114*7c478bd9Sstevel@tonic-gate  *	and guarantee that buffer we give to VOP_READDIR will be large enough
115*7c478bd9Sstevel@tonic-gate  *	to hold ., .., and the largest possible solaris dirent64.
116*7c478bd9Sstevel@tonic-gate  */
117*7c478bd9Sstevel@tonic-gate #define	RFS4_MINLEN_ENTRY4 36
118*7c478bd9Sstevel@tonic-gate #define	RFS4_MINLEN_RDDIR4 (4 + NFS4_VERIFIER_SIZE + 4 + RFS4_MINLEN_ENTRY4 + 4)
119*7c478bd9Sstevel@tonic-gate #define	RFS4_MINLEN_RDDIR_BUF \
120*7c478bd9Sstevel@tonic-gate 	(DIRENT64_RECLEN(1) + DIRENT64_RECLEN(2) + DIRENT64_RECLEN(MAXNAMELEN))
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate /*
123*7c478bd9Sstevel@tonic-gate  * It would be better to pad to 4 bytes since that's what XDR would do,
124*7c478bd9Sstevel@tonic-gate  * but the dirents UFS gives us are already padded to 8, so just take
125*7c478bd9Sstevel@tonic-gate  * what we're given.  Dircount is only a hint anyway.  Currently the
126*7c478bd9Sstevel@tonic-gate  * solaris kernel is ASCII only, so there's no point in calling the
127*7c478bd9Sstevel@tonic-gate  * UTF8 functions.
128*7c478bd9Sstevel@tonic-gate  *
129*7c478bd9Sstevel@tonic-gate  * dirent64: named padded to provide 8 byte struct alignment
130*7c478bd9Sstevel@tonic-gate  *	d_ino(8) + d_off(8) + d_reclen(2) + d_name(namelen + null(1) + pad)
131*7c478bd9Sstevel@tonic-gate  *
132*7c478bd9Sstevel@tonic-gate  * cookie: uint64_t   +  utf8namelen: uint_t  +   utf8name padded to 8 bytes
133*7c478bd9Sstevel@tonic-gate  *
134*7c478bd9Sstevel@tonic-gate  */
135*7c478bd9Sstevel@tonic-gate #define	DIRENT64_TO_DIRCOUNT(dp) \
136*7c478bd9Sstevel@tonic-gate 	(3 * BYTES_PER_XDR_UNIT + DIRENT64_NAMELEN((dp)->d_reclen))
137*7c478bd9Sstevel@tonic-gate 
138*7c478bd9Sstevel@tonic-gate time_t rfs4_start_time;			/* Initialized in rfs4_srvrinit */
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate static sysid_t lockt_sysid;		/* dummy sysid for all LOCKT calls */
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate u_longlong_t nfs4_srv_caller_id;
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate verifier4	Write4verf;
145*7c478bd9Sstevel@tonic-gate verifier4	Readdir4verf;
146*7c478bd9Sstevel@tonic-gate 
147*7c478bd9Sstevel@tonic-gate void		rfs4_init_compound_state(struct compound_state *);
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate static void	nullfree(caddr_t);
150*7c478bd9Sstevel@tonic-gate static void	rfs4_op_inval(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
151*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
152*7c478bd9Sstevel@tonic-gate static void	rfs4_op_access(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
153*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
154*7c478bd9Sstevel@tonic-gate static void	rfs4_op_close(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
155*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
156*7c478bd9Sstevel@tonic-gate static void	rfs4_op_commit(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
157*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
158*7c478bd9Sstevel@tonic-gate static void	rfs4_op_create(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
159*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
160*7c478bd9Sstevel@tonic-gate static void	rfs4_op_create_free(nfs_resop4 *resop);
161*7c478bd9Sstevel@tonic-gate static void	rfs4_op_delegreturn(nfs_argop4 *, nfs_resop4 *,
162*7c478bd9Sstevel@tonic-gate 				    struct svc_req *, struct compound_state *);
163*7c478bd9Sstevel@tonic-gate static void	rfs4_op_getattr(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
164*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
165*7c478bd9Sstevel@tonic-gate static void	rfs4_op_getattr_free(nfs_resop4 *);
166*7c478bd9Sstevel@tonic-gate static void	rfs4_op_getfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
167*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
168*7c478bd9Sstevel@tonic-gate static void	rfs4_op_getfh_free(nfs_resop4 *);
169*7c478bd9Sstevel@tonic-gate static void	rfs4_op_illegal(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
170*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
171*7c478bd9Sstevel@tonic-gate static void	rfs4_op_link(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
172*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
173*7c478bd9Sstevel@tonic-gate static void	rfs4_op_lock(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
174*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
175*7c478bd9Sstevel@tonic-gate static void	lock_denied_free(nfs_resop4 *);
176*7c478bd9Sstevel@tonic-gate static void	rfs4_op_locku(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
177*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
178*7c478bd9Sstevel@tonic-gate static void	rfs4_op_lockt(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
179*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
180*7c478bd9Sstevel@tonic-gate static void	rfs4_op_lookup(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
181*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
182*7c478bd9Sstevel@tonic-gate static void	rfs4_op_lookupp(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
183*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
184*7c478bd9Sstevel@tonic-gate static void	rfs4_op_openattr(nfs_argop4 *argop, nfs_resop4 *resop,
185*7c478bd9Sstevel@tonic-gate 				struct svc_req *req, struct compound_state *cs);
186*7c478bd9Sstevel@tonic-gate static void	rfs4_op_nverify(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
187*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
188*7c478bd9Sstevel@tonic-gate static void	rfs4_op_open(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
189*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
190*7c478bd9Sstevel@tonic-gate static void	rfs4_op_open_confirm(nfs_argop4 *, nfs_resop4 *,
191*7c478bd9Sstevel@tonic-gate 			struct svc_req *, struct compound_state *);
192*7c478bd9Sstevel@tonic-gate static void	rfs4_op_open_downgrade(nfs_argop4 *, nfs_resop4 *,
193*7c478bd9Sstevel@tonic-gate 			struct svc_req *, struct compound_state *);
194*7c478bd9Sstevel@tonic-gate static void	rfs4_op_putfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
195*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
196*7c478bd9Sstevel@tonic-gate static void	rfs4_op_putpubfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
197*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
198*7c478bd9Sstevel@tonic-gate static void	rfs4_op_putrootfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
199*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
200*7c478bd9Sstevel@tonic-gate static void	rfs4_op_read(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
201*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
202*7c478bd9Sstevel@tonic-gate static void	rfs4_op_read_free(nfs_resop4 *);
203*7c478bd9Sstevel@tonic-gate static void	rfs4_op_readdir_free(nfs_resop4 *resop);
204*7c478bd9Sstevel@tonic-gate static void	rfs4_op_readlink(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
205*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
206*7c478bd9Sstevel@tonic-gate static void	rfs4_op_readlink_free(nfs_resop4 *);
207*7c478bd9Sstevel@tonic-gate static void	rfs4_op_release_lockowner(nfs_argop4 *, nfs_resop4 *,
208*7c478bd9Sstevel@tonic-gate 			struct svc_req *, struct compound_state *);
209*7c478bd9Sstevel@tonic-gate static void	rfs4_op_remove(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
210*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
211*7c478bd9Sstevel@tonic-gate static void	rfs4_op_rename(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
212*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
213*7c478bd9Sstevel@tonic-gate static void	rfs4_op_renew(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
214*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
215*7c478bd9Sstevel@tonic-gate static void	rfs4_op_restorefh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
216*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
217*7c478bd9Sstevel@tonic-gate static void	rfs4_op_savefh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
218*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
219*7c478bd9Sstevel@tonic-gate static void	rfs4_op_setattr(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
220*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
221*7c478bd9Sstevel@tonic-gate static void	rfs4_op_verify(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
222*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
223*7c478bd9Sstevel@tonic-gate static void	rfs4_op_write(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
224*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
225*7c478bd9Sstevel@tonic-gate static void	rfs4_op_setclientid(nfs_argop4 *, nfs_resop4 *,
226*7c478bd9Sstevel@tonic-gate 			struct svc_req *, struct compound_state *);
227*7c478bd9Sstevel@tonic-gate static void	rfs4_op_setclientid_confirm(nfs_argop4 *, nfs_resop4 *,
228*7c478bd9Sstevel@tonic-gate 			struct svc_req *req, struct compound_state *);
229*7c478bd9Sstevel@tonic-gate static void	rfs4_op_secinfo(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
230*7c478bd9Sstevel@tonic-gate 			struct compound_state *);
231*7c478bd9Sstevel@tonic-gate static void	rfs4_op_secinfo_free(nfs_resop4 *);
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate static nfsstat4 check_open_access(uint32_t,
234*7c478bd9Sstevel@tonic-gate 				struct compound_state *, struct svc_req *);
235*7c478bd9Sstevel@tonic-gate nfsstat4 rfs4_client_sysid(rfs4_client_t *, sysid_t *);
236*7c478bd9Sstevel@tonic-gate static int	vop_shrlock(vnode_t *, int, struct shrlock *, int);
237*7c478bd9Sstevel@tonic-gate static int 	rfs4_shrlock(rfs4_state_t *, int);
238*7c478bd9Sstevel@tonic-gate static int	rfs4_share(rfs4_state_t *);
239*7c478bd9Sstevel@tonic-gate void rfs4_ss_clid(rfs4_client_t *, struct svc_req *);
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate /*
242*7c478bd9Sstevel@tonic-gate  * translation table for attrs
243*7c478bd9Sstevel@tonic-gate  */
244*7c478bd9Sstevel@tonic-gate struct nfs4_ntov_table {
245*7c478bd9Sstevel@tonic-gate 	union nfs4_attr_u *na;
246*7c478bd9Sstevel@tonic-gate 	uint8_t amap[NFS4_MAXNUM_ATTRS];
247*7c478bd9Sstevel@tonic-gate 	int attrcnt;
248*7c478bd9Sstevel@tonic-gate 	bool_t vfsstat;
249*7c478bd9Sstevel@tonic-gate };
250*7c478bd9Sstevel@tonic-gate 
251*7c478bd9Sstevel@tonic-gate static void	nfs4_ntov_table_init(struct nfs4_ntov_table *ntovp);
252*7c478bd9Sstevel@tonic-gate static void	nfs4_ntov_table_free(struct nfs4_ntov_table *ntovp,
253*7c478bd9Sstevel@tonic-gate 				    struct nfs4_svgetit_arg *sargp);
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate static nfsstat4	do_rfs4_set_attrs(bitmap4 *resp, fattr4 *fattrp,
256*7c478bd9Sstevel@tonic-gate 		    struct compound_state *cs, struct nfs4_svgetit_arg *sargp,
257*7c478bd9Sstevel@tonic-gate 		    struct nfs4_ntov_table *ntovp, nfs4_attr_cmd_t cmd);
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate fem_t	*deleg_rdops;
260*7c478bd9Sstevel@tonic-gate fem_t	*deleg_wrops;
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate rfs4_servinst_t	*rfs4_cur_servinst = NULL;	/* current server instance */
263*7c478bd9Sstevel@tonic-gate kmutex_t	rfs4_servinst_lock;		/* protects linked list */
264*7c478bd9Sstevel@tonic-gate int		rfs4_seen_first_compound;	/* set first time we see one */
265*7c478bd9Sstevel@tonic-gate 
266*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
267*7c478bd9Sstevel@tonic-gate int	rfs4_servinst_debug = 0;
268*7c478bd9Sstevel@tonic-gate #endif
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate /*
271*7c478bd9Sstevel@tonic-gate  * NFS4 op dispatch table
272*7c478bd9Sstevel@tonic-gate  */
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate struct rfsv4disp {
275*7c478bd9Sstevel@tonic-gate 	void	(*dis_proc)();		/* proc to call */
276*7c478bd9Sstevel@tonic-gate 	void	(*dis_resfree)();	/* frees space allocated by proc */
277*7c478bd9Sstevel@tonic-gate 	int	dis_flags;		/* RPC_IDEMPOTENT, etc... */
278*7c478bd9Sstevel@tonic-gate };
279*7c478bd9Sstevel@tonic-gate 
280*7c478bd9Sstevel@tonic-gate static struct rfsv4disp rfsv4disptab[] = {
281*7c478bd9Sstevel@tonic-gate 	/*
282*7c478bd9Sstevel@tonic-gate 	 * NFS VERSION 4
283*7c478bd9Sstevel@tonic-gate 	 */
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate 	/* RFS_NULL = 0 */
286*7c478bd9Sstevel@tonic-gate 	{rfs4_op_illegal, nullfree, 0},
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 	/* UNUSED = 1 */
289*7c478bd9Sstevel@tonic-gate 	{rfs4_op_illegal, nullfree, 0},
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 	/* UNUSED = 2 */
292*7c478bd9Sstevel@tonic-gate 	{rfs4_op_illegal, nullfree, 0},
293*7c478bd9Sstevel@tonic-gate 
294*7c478bd9Sstevel@tonic-gate 	/* OP_ACCESS = 3 */
295*7c478bd9Sstevel@tonic-gate 	{rfs4_op_access, nullfree, RPC_IDEMPOTENT},
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 	/* OP_CLOSE = 4 */
298*7c478bd9Sstevel@tonic-gate 	{rfs4_op_close, nullfree, 0},
299*7c478bd9Sstevel@tonic-gate 
300*7c478bd9Sstevel@tonic-gate 	/* OP_COMMIT = 5 */
301*7c478bd9Sstevel@tonic-gate 	{rfs4_op_commit, nullfree, RPC_IDEMPOTENT},
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	/* OP_CREATE = 6 */
304*7c478bd9Sstevel@tonic-gate 	{rfs4_op_create, nullfree, 0},
305*7c478bd9Sstevel@tonic-gate 
306*7c478bd9Sstevel@tonic-gate 	/* OP_DELEGPURGE = 7 */
307*7c478bd9Sstevel@tonic-gate 	{rfs4_op_inval, nullfree, 0},
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 	/* OP_DELEGRETURN = 8 */
310*7c478bd9Sstevel@tonic-gate 	{rfs4_op_delegreturn, nullfree, 0},
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	/* OP_GETATTR = 9 */
313*7c478bd9Sstevel@tonic-gate 	{rfs4_op_getattr, rfs4_op_getattr_free, RPC_IDEMPOTENT},
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate 	/* OP_GETFH = 10 */
316*7c478bd9Sstevel@tonic-gate 	{rfs4_op_getfh, rfs4_op_getfh_free, RPC_ALL},
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 	/* OP_LINK = 11 */
319*7c478bd9Sstevel@tonic-gate 	{rfs4_op_link, nullfree, 0},
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	/* OP_LOCK = 12 */
322*7c478bd9Sstevel@tonic-gate 	{rfs4_op_lock, lock_denied_free, 0},
323*7c478bd9Sstevel@tonic-gate 
324*7c478bd9Sstevel@tonic-gate 	/* OP_LOCKT = 13 */
325*7c478bd9Sstevel@tonic-gate 	{rfs4_op_lockt, lock_denied_free, 0},
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 	/* OP_LOCKU = 14 */
328*7c478bd9Sstevel@tonic-gate 	{rfs4_op_locku, nullfree, 0},
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate 	/* OP_LOOKUP = 15 */
331*7c478bd9Sstevel@tonic-gate 	{rfs4_op_lookup, nullfree, (RPC_IDEMPOTENT|RPC_PUBLICFH_OK)},
332*7c478bd9Sstevel@tonic-gate 
333*7c478bd9Sstevel@tonic-gate 	/* OP_LOOKUPP = 16 */
334*7c478bd9Sstevel@tonic-gate 	{rfs4_op_lookupp, nullfree, (RPC_IDEMPOTENT|RPC_PUBLICFH_OK)},
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate 	/* OP_NVERIFY = 17 */
337*7c478bd9Sstevel@tonic-gate 	{rfs4_op_nverify, nullfree, RPC_IDEMPOTENT},
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 	/* OP_OPEN = 18 */
340*7c478bd9Sstevel@tonic-gate 	{rfs4_op_open, rfs4_free_reply, 0},
341*7c478bd9Sstevel@tonic-gate 
342*7c478bd9Sstevel@tonic-gate 	/* OP_OPENATTR = 19 */
343*7c478bd9Sstevel@tonic-gate 	{rfs4_op_openattr, nullfree, 0},
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 	/* OP_OPEN_CONFIRM = 20 */
346*7c478bd9Sstevel@tonic-gate 	{rfs4_op_open_confirm, nullfree, 0},
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate 	/* OP_OPEN_DOWNGRADE = 21 */
349*7c478bd9Sstevel@tonic-gate 	{rfs4_op_open_downgrade, nullfree, 0},
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	/* OP_OPEN_PUTFH = 22 */
352*7c478bd9Sstevel@tonic-gate 	{rfs4_op_putfh, nullfree, RPC_ALL},
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	/* OP_PUTPUBFH = 23 */
355*7c478bd9Sstevel@tonic-gate 	{rfs4_op_putpubfh, nullfree, RPC_ALL},
356*7c478bd9Sstevel@tonic-gate 
357*7c478bd9Sstevel@tonic-gate 	/* OP_PUTROOTFH = 24 */
358*7c478bd9Sstevel@tonic-gate 	{rfs4_op_putrootfh, nullfree, RPC_ALL},
359*7c478bd9Sstevel@tonic-gate 
360*7c478bd9Sstevel@tonic-gate 	/* OP_READ = 25 */
361*7c478bd9Sstevel@tonic-gate 	{rfs4_op_read, rfs4_op_read_free, RPC_IDEMPOTENT},
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate 	/* OP_READDIR = 26 */
364*7c478bd9Sstevel@tonic-gate 	{rfs4_op_readdir, rfs4_op_readdir_free, RPC_IDEMPOTENT},
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 	/* OP_READLINK = 27 */
367*7c478bd9Sstevel@tonic-gate 	{rfs4_op_readlink, rfs4_op_readlink_free, RPC_IDEMPOTENT},
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	/* OP_REMOVE = 28 */
370*7c478bd9Sstevel@tonic-gate 	{rfs4_op_remove, nullfree, 0},
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 	/* OP_RENAME = 29 */
373*7c478bd9Sstevel@tonic-gate 	{rfs4_op_rename, nullfree, 0},
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate 	/* OP_RENEW = 30 */
376*7c478bd9Sstevel@tonic-gate 	{rfs4_op_renew, nullfree, 0},
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 	/* OP_RESTOREFH = 31 */
379*7c478bd9Sstevel@tonic-gate 	{rfs4_op_restorefh, nullfree, RPC_ALL},
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 	/* OP_SAVEFH = 32 */
382*7c478bd9Sstevel@tonic-gate 	{rfs4_op_savefh, nullfree, RPC_ALL},
383*7c478bd9Sstevel@tonic-gate 
384*7c478bd9Sstevel@tonic-gate 	/* OP_SECINFO = 33 */
385*7c478bd9Sstevel@tonic-gate 	{rfs4_op_secinfo, rfs4_op_secinfo_free, 0},
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 	/* OP_SETATTR = 34 */
388*7c478bd9Sstevel@tonic-gate 	{rfs4_op_setattr, nullfree, 0},
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 	/* OP_SETCLIENTID = 35 */
391*7c478bd9Sstevel@tonic-gate 	{rfs4_op_setclientid, nullfree, 0},
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate 	/* OP_SETCLIENTID_CONFIRM = 36 */
394*7c478bd9Sstevel@tonic-gate 	{rfs4_op_setclientid_confirm, nullfree, 0},
395*7c478bd9Sstevel@tonic-gate 
396*7c478bd9Sstevel@tonic-gate 	/* OP_VERIFY = 37 */
397*7c478bd9Sstevel@tonic-gate 	{rfs4_op_verify, nullfree, RPC_IDEMPOTENT},
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 	/* OP_WRITE = 38 */
400*7c478bd9Sstevel@tonic-gate 	{rfs4_op_write, nullfree, 0},
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate 	/* OP_RELEASE_LOCKOWNER = 39 */
403*7c478bd9Sstevel@tonic-gate 	{rfs4_op_release_lockowner, nullfree, 0},
404*7c478bd9Sstevel@tonic-gate };
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate static uint_t rfsv4disp_cnt = sizeof (rfsv4disptab) / sizeof (rfsv4disptab[0]);
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate #define	OP_ILLEGAL_IDX (rfsv4disp_cnt)
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
411*7c478bd9Sstevel@tonic-gate 
412*7c478bd9Sstevel@tonic-gate int rfs4_fillone_debug = 0;
413*7c478bd9Sstevel@tonic-gate int rfs4_shrlock_debug = 0;
414*7c478bd9Sstevel@tonic-gate int rfs4_no_stub_access = 1;
415*7c478bd9Sstevel@tonic-gate int rfs4_rddir_debug = 0;
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate static char *rfs4_op_string[] = {
418*7c478bd9Sstevel@tonic-gate 	"rfs4_op_null",
419*7c478bd9Sstevel@tonic-gate 	"rfs4_op_1 unused",
420*7c478bd9Sstevel@tonic-gate 	"rfs4_op_2 unused",
421*7c478bd9Sstevel@tonic-gate 	"rfs4_op_access",
422*7c478bd9Sstevel@tonic-gate 	"rfs4_op_close",
423*7c478bd9Sstevel@tonic-gate 	"rfs4_op_commit",
424*7c478bd9Sstevel@tonic-gate 	"rfs4_op_create",
425*7c478bd9Sstevel@tonic-gate 	"rfs4_op_delegpurge",
426*7c478bd9Sstevel@tonic-gate 	"rfs4_op_delegreturn",
427*7c478bd9Sstevel@tonic-gate 	"rfs4_op_getattr",
428*7c478bd9Sstevel@tonic-gate 	"rfs4_op_getfh",
429*7c478bd9Sstevel@tonic-gate 	"rfs4_op_link",
430*7c478bd9Sstevel@tonic-gate 	"rfs4_op_lock",
431*7c478bd9Sstevel@tonic-gate 	"rfs4_op_lockt",
432*7c478bd9Sstevel@tonic-gate 	"rfs4_op_locku",
433*7c478bd9Sstevel@tonic-gate 	"rfs4_op_lookup",
434*7c478bd9Sstevel@tonic-gate 	"rfs4_op_lookupp",
435*7c478bd9Sstevel@tonic-gate 	"rfs4_op_nverify",
436*7c478bd9Sstevel@tonic-gate 	"rfs4_op_open",
437*7c478bd9Sstevel@tonic-gate 	"rfs4_op_openattr",
438*7c478bd9Sstevel@tonic-gate 	"rfs4_op_open_confirm",
439*7c478bd9Sstevel@tonic-gate 	"rfs4_op_open_downgrade",
440*7c478bd9Sstevel@tonic-gate 	"rfs4_op_putfh",
441*7c478bd9Sstevel@tonic-gate 	"rfs4_op_putpubfh",
442*7c478bd9Sstevel@tonic-gate 	"rfs4_op_putrootfh",
443*7c478bd9Sstevel@tonic-gate 	"rfs4_op_read",
444*7c478bd9Sstevel@tonic-gate 	"rfs4_op_readdir",
445*7c478bd9Sstevel@tonic-gate 	"rfs4_op_readlink",
446*7c478bd9Sstevel@tonic-gate 	"rfs4_op_remove",
447*7c478bd9Sstevel@tonic-gate 	"rfs4_op_rename",
448*7c478bd9Sstevel@tonic-gate 	"rfs4_op_renew",
449*7c478bd9Sstevel@tonic-gate 	"rfs4_op_restorefh",
450*7c478bd9Sstevel@tonic-gate 	"rfs4_op_savefh",
451*7c478bd9Sstevel@tonic-gate 	"rfs4_op_secinfo",
452*7c478bd9Sstevel@tonic-gate 	"rfs4_op_setattr",
453*7c478bd9Sstevel@tonic-gate 	"rfs4_op_setclientid",
454*7c478bd9Sstevel@tonic-gate 	"rfs4_op_setclient_confirm",
455*7c478bd9Sstevel@tonic-gate 	"rfs4_op_verify",
456*7c478bd9Sstevel@tonic-gate 	"rfs4_op_write",
457*7c478bd9Sstevel@tonic-gate 	"rfs4_op_release_lockowner",
458*7c478bd9Sstevel@tonic-gate 	"rfs4_op_illegal"
459*7c478bd9Sstevel@tonic-gate };
460*7c478bd9Sstevel@tonic-gate #endif
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate void rfs4_ss_chkclid(rfs4_client_t *);
463*7c478bd9Sstevel@tonic-gate 
464*7c478bd9Sstevel@tonic-gate #ifdef	nextdp
465*7c478bd9Sstevel@tonic-gate #undef nextdp
466*7c478bd9Sstevel@tonic-gate #endif
467*7c478bd9Sstevel@tonic-gate #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
468*7c478bd9Sstevel@tonic-gate 
469*7c478bd9Sstevel@tonic-gate static const fs_operation_def_t nfs4_rd_deleg_tmpl[] = {
470*7c478bd9Sstevel@tonic-gate 	VOPNAME_OPEN, deleg_rdopen,
471*7c478bd9Sstevel@tonic-gate 	VOPNAME_WRITE, deleg_write,
472*7c478bd9Sstevel@tonic-gate 	VOPNAME_SETATTR, deleg_setattr,
473*7c478bd9Sstevel@tonic-gate 	VOPNAME_RWLOCK, deleg_rd_rwlock,
474*7c478bd9Sstevel@tonic-gate 	VOPNAME_SPACE, deleg_space,
475*7c478bd9Sstevel@tonic-gate 	VOPNAME_SETSECATTR, deleg_setsecattr,
476*7c478bd9Sstevel@tonic-gate 	VOPNAME_VNEVENT, deleg_vnevent,
477*7c478bd9Sstevel@tonic-gate 	NULL, NULL
478*7c478bd9Sstevel@tonic-gate };
479*7c478bd9Sstevel@tonic-gate static const fs_operation_def_t nfs4_wr_deleg_tmpl[] = {
480*7c478bd9Sstevel@tonic-gate 	VOPNAME_OPEN, deleg_wropen,
481*7c478bd9Sstevel@tonic-gate 	VOPNAME_READ, deleg_read,
482*7c478bd9Sstevel@tonic-gate 	VOPNAME_WRITE, deleg_write,
483*7c478bd9Sstevel@tonic-gate 	VOPNAME_SETATTR, deleg_setattr,
484*7c478bd9Sstevel@tonic-gate 	VOPNAME_RWLOCK, deleg_wr_rwlock,
485*7c478bd9Sstevel@tonic-gate 	VOPNAME_SPACE, deleg_space,
486*7c478bd9Sstevel@tonic-gate 	VOPNAME_SETSECATTR, deleg_setsecattr,
487*7c478bd9Sstevel@tonic-gate 	VOPNAME_VNEVENT, deleg_vnevent,
488*7c478bd9Sstevel@tonic-gate 	NULL, NULL
489*7c478bd9Sstevel@tonic-gate };
490*7c478bd9Sstevel@tonic-gate 
491*7c478bd9Sstevel@tonic-gate int
492*7c478bd9Sstevel@tonic-gate rfs4_srvrinit(void)
493*7c478bd9Sstevel@tonic-gate {
494*7c478bd9Sstevel@tonic-gate 	timespec32_t verf;
495*7c478bd9Sstevel@tonic-gate 	int error;
496*7c478bd9Sstevel@tonic-gate 	extern void rfs4_attr_init();
497*7c478bd9Sstevel@tonic-gate 	extern krwlock_t rfs4_deleg_policy_lock;
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate 	/*
500*7c478bd9Sstevel@tonic-gate 	 * The following algorithm attempts to find a unique verifier
501*7c478bd9Sstevel@tonic-gate 	 * to be used as the write verifier returned from the server
502*7c478bd9Sstevel@tonic-gate 	 * to the client.  It is important that this verifier change
503*7c478bd9Sstevel@tonic-gate 	 * whenever the server reboots.  Of secondary importance, it
504*7c478bd9Sstevel@tonic-gate 	 * is important for the verifier to be unique between two
505*7c478bd9Sstevel@tonic-gate 	 * different servers.
506*7c478bd9Sstevel@tonic-gate 	 *
507*7c478bd9Sstevel@tonic-gate 	 * Thus, an attempt is made to use the system hostid and the
508*7c478bd9Sstevel@tonic-gate 	 * current time in seconds when the nfssrv kernel module is
509*7c478bd9Sstevel@tonic-gate 	 * loaded.  It is assumed that an NFS server will not be able
510*7c478bd9Sstevel@tonic-gate 	 * to boot and then to reboot in less than a second.  If the
511*7c478bd9Sstevel@tonic-gate 	 * hostid has not been set, then the current high resolution
512*7c478bd9Sstevel@tonic-gate 	 * time is used.  This will ensure different verifiers each
513*7c478bd9Sstevel@tonic-gate 	 * time the server reboots and minimize the chances that two
514*7c478bd9Sstevel@tonic-gate 	 * different servers will have the same verifier.
515*7c478bd9Sstevel@tonic-gate 	 * XXX - this is broken on LP64 kernels.
516*7c478bd9Sstevel@tonic-gate 	 */
517*7c478bd9Sstevel@tonic-gate 	verf.tv_sec = (time_t)nfs_atoi(hw_serial);
518*7c478bd9Sstevel@tonic-gate 	if (verf.tv_sec != 0) {
519*7c478bd9Sstevel@tonic-gate 		verf.tv_nsec = gethrestime_sec();
520*7c478bd9Sstevel@tonic-gate 	} else {
521*7c478bd9Sstevel@tonic-gate 		timespec_t tverf;
522*7c478bd9Sstevel@tonic-gate 
523*7c478bd9Sstevel@tonic-gate 		gethrestime(&tverf);
524*7c478bd9Sstevel@tonic-gate 		verf.tv_sec = (time_t)tverf.tv_sec;
525*7c478bd9Sstevel@tonic-gate 		verf.tv_nsec = tverf.tv_nsec;
526*7c478bd9Sstevel@tonic-gate 	}
527*7c478bd9Sstevel@tonic-gate 
528*7c478bd9Sstevel@tonic-gate 	Write4verf = *(uint64_t *)&verf;
529*7c478bd9Sstevel@tonic-gate 
530*7c478bd9Sstevel@tonic-gate 	rfs4_attr_init();
531*7c478bd9Sstevel@tonic-gate 	mutex_init(&rfs4_deleg_lock, NULL, MUTEX_DEFAULT, NULL);
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 	/* Used to manage create/destroy of server state */
534*7c478bd9Sstevel@tonic-gate 	mutex_init(&rfs4_state_lock, NULL, MUTEX_DEFAULT, NULL);
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 	/* Used to manage access to server instance linked list */
537*7c478bd9Sstevel@tonic-gate 	mutex_init(&rfs4_servinst_lock, NULL, MUTEX_DEFAULT, NULL);
538*7c478bd9Sstevel@tonic-gate 
539*7c478bd9Sstevel@tonic-gate 	/* Used to manage access to rfs4_deleg_policy */
540*7c478bd9Sstevel@tonic-gate 	rw_init(&rfs4_deleg_policy_lock, NULL, RW_DEFAULT, NULL);
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	error = fem_create("deleg_rdops", nfs4_rd_deleg_tmpl, &deleg_rdops);
543*7c478bd9Sstevel@tonic-gate 	if (error != 0) {
544*7c478bd9Sstevel@tonic-gate 		rfs4_disable_delegation();
545*7c478bd9Sstevel@tonic-gate 	} else {
546*7c478bd9Sstevel@tonic-gate 		error = fem_create("deleg_wrops", nfs4_wr_deleg_tmpl,
547*7c478bd9Sstevel@tonic-gate 				&deleg_wrops);
548*7c478bd9Sstevel@tonic-gate 		if (error != 0) {
549*7c478bd9Sstevel@tonic-gate 			rfs4_disable_delegation();
550*7c478bd9Sstevel@tonic-gate 			fem_free(deleg_rdops);
551*7c478bd9Sstevel@tonic-gate 		}
552*7c478bd9Sstevel@tonic-gate 	}
553*7c478bd9Sstevel@tonic-gate 
554*7c478bd9Sstevel@tonic-gate 	nfs4_srv_caller_id = fs_new_caller_id();
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 	lockt_sysid = lm_alloc_sysidt();
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate 	return (0);
559*7c478bd9Sstevel@tonic-gate }
560*7c478bd9Sstevel@tonic-gate 
561*7c478bd9Sstevel@tonic-gate void
562*7c478bd9Sstevel@tonic-gate rfs4_srvrfini(void)
563*7c478bd9Sstevel@tonic-gate {
564*7c478bd9Sstevel@tonic-gate 	extern krwlock_t rfs4_deleg_policy_lock;
565*7c478bd9Sstevel@tonic-gate 
566*7c478bd9Sstevel@tonic-gate 	if (lockt_sysid != LM_NOSYSID) {
567*7c478bd9Sstevel@tonic-gate 		lm_free_sysidt(lockt_sysid);
568*7c478bd9Sstevel@tonic-gate 		lockt_sysid = LM_NOSYSID;
569*7c478bd9Sstevel@tonic-gate 	}
570*7c478bd9Sstevel@tonic-gate 
571*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&rfs4_deleg_lock);
572*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&rfs4_state_lock);
573*7c478bd9Sstevel@tonic-gate 	rw_destroy(&rfs4_deleg_policy_lock);
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate 	fem_free(deleg_rdops);
576*7c478bd9Sstevel@tonic-gate 	fem_free(deleg_wrops);
577*7c478bd9Sstevel@tonic-gate }
578*7c478bd9Sstevel@tonic-gate 
579*7c478bd9Sstevel@tonic-gate void
580*7c478bd9Sstevel@tonic-gate rfs4_init_compound_state(struct compound_state *cs)
581*7c478bd9Sstevel@tonic-gate {
582*7c478bd9Sstevel@tonic-gate 	bzero(cs, sizeof (*cs));
583*7c478bd9Sstevel@tonic-gate 	cs->cont = TRUE;
584*7c478bd9Sstevel@tonic-gate 	cs->access = CS_ACCESS_DENIED;
585*7c478bd9Sstevel@tonic-gate 	cs->deleg = FALSE;
586*7c478bd9Sstevel@tonic-gate 	cs->mandlock = FALSE;
587*7c478bd9Sstevel@tonic-gate 	cs->fh.nfs_fh4_val = cs->fhbuf;
588*7c478bd9Sstevel@tonic-gate }
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate void
591*7c478bd9Sstevel@tonic-gate rfs4_grace_start(rfs4_servinst_t *sip)
592*7c478bd9Sstevel@tonic-gate {
593*7c478bd9Sstevel@tonic-gate 	time_t now = gethrestime_sec();
594*7c478bd9Sstevel@tonic-gate 
595*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
596*7c478bd9Sstevel@tonic-gate 	    "rfs4_grace_start: inst %p: 0x%lx", (void *)sip, now));
597*7c478bd9Sstevel@tonic-gate 
598*7c478bd9Sstevel@tonic-gate 	rw_enter(&sip->rwlock, RW_WRITER);
599*7c478bd9Sstevel@tonic-gate 	sip->start_time = now;
600*7c478bd9Sstevel@tonic-gate 	sip->grace_period = rfs4_grace_period;
601*7c478bd9Sstevel@tonic-gate 	rw_exit(&sip->rwlock);
602*7c478bd9Sstevel@tonic-gate }
603*7c478bd9Sstevel@tonic-gate 
604*7c478bd9Sstevel@tonic-gate /*
605*7c478bd9Sstevel@tonic-gate  * returns true if the instance's grace period has never been started
606*7c478bd9Sstevel@tonic-gate  */
607*7c478bd9Sstevel@tonic-gate int
608*7c478bd9Sstevel@tonic-gate rfs4_servinst_grace_new(rfs4_servinst_t *sip)
609*7c478bd9Sstevel@tonic-gate {
610*7c478bd9Sstevel@tonic-gate 	time_t start_time;
611*7c478bd9Sstevel@tonic-gate 
612*7c478bd9Sstevel@tonic-gate 	rw_enter(&sip->rwlock, RW_READER);
613*7c478bd9Sstevel@tonic-gate 	start_time = sip->start_time;
614*7c478bd9Sstevel@tonic-gate 	rw_exit(&sip->rwlock);
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate 	return (start_time == 0);
617*7c478bd9Sstevel@tonic-gate }
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate /*
620*7c478bd9Sstevel@tonic-gate  * Indicates if server instance is within the
621*7c478bd9Sstevel@tonic-gate  * grace period.
622*7c478bd9Sstevel@tonic-gate  */
623*7c478bd9Sstevel@tonic-gate int
624*7c478bd9Sstevel@tonic-gate rfs4_servinst_in_grace(rfs4_servinst_t *sip)
625*7c478bd9Sstevel@tonic-gate {
626*7c478bd9Sstevel@tonic-gate 	time_t grace_expiry;
627*7c478bd9Sstevel@tonic-gate 
628*7c478bd9Sstevel@tonic-gate 	rw_enter(&sip->rwlock, RW_READER);
629*7c478bd9Sstevel@tonic-gate 	grace_expiry = sip->start_time + sip->grace_period;
630*7c478bd9Sstevel@tonic-gate 	rw_exit(&sip->rwlock);
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate 	return (gethrestime_sec() < grace_expiry);
633*7c478bd9Sstevel@tonic-gate }
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate int
636*7c478bd9Sstevel@tonic-gate rfs4_clnt_in_grace(rfs4_client_t *cp)
637*7c478bd9Sstevel@tonic-gate {
638*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_refcnt(cp->dbe) > 0);
639*7c478bd9Sstevel@tonic-gate 
640*7c478bd9Sstevel@tonic-gate 	return (rfs4_servinst_in_grace(cp->server_instance));
641*7c478bd9Sstevel@tonic-gate }
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate /*
644*7c478bd9Sstevel@tonic-gate  * reset all currently active grace periods
645*7c478bd9Sstevel@tonic-gate  */
646*7c478bd9Sstevel@tonic-gate void
647*7c478bd9Sstevel@tonic-gate rfs4_grace_reset_all(void)
648*7c478bd9Sstevel@tonic-gate {
649*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
650*7c478bd9Sstevel@tonic-gate 	int n = 0;
651*7c478bd9Sstevel@tonic-gate #endif
652*7c478bd9Sstevel@tonic-gate 	rfs4_servinst_t *sip;
653*7c478bd9Sstevel@tonic-gate 
654*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_servinst_lock);
655*7c478bd9Sstevel@tonic-gate 	for (sip = rfs4_cur_servinst; sip != NULL; sip = sip->prev) {
656*7c478bd9Sstevel@tonic-gate 		if (rfs4_servinst_in_grace(sip)) {
657*7c478bd9Sstevel@tonic-gate 			rfs4_grace_start(sip);
658*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
659*7c478bd9Sstevel@tonic-gate 			n++;
660*7c478bd9Sstevel@tonic-gate #endif
661*7c478bd9Sstevel@tonic-gate 		}
662*7c478bd9Sstevel@tonic-gate 	}
663*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_servinst_lock);
664*7c478bd9Sstevel@tonic-gate 
665*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
666*7c478bd9Sstevel@tonic-gate 	    "rfs4_grace_reset_all: reset %d instances", n));
667*7c478bd9Sstevel@tonic-gate }
668*7c478bd9Sstevel@tonic-gate 
669*7c478bd9Sstevel@tonic-gate /*
670*7c478bd9Sstevel@tonic-gate  * start any new instances' grace periods
671*7c478bd9Sstevel@tonic-gate  */
672*7c478bd9Sstevel@tonic-gate void
673*7c478bd9Sstevel@tonic-gate rfs4_grace_start_new(void)
674*7c478bd9Sstevel@tonic-gate {
675*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
676*7c478bd9Sstevel@tonic-gate 	int n = 0;
677*7c478bd9Sstevel@tonic-gate #endif
678*7c478bd9Sstevel@tonic-gate 	rfs4_servinst_t *sip;
679*7c478bd9Sstevel@tonic-gate 
680*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_servinst_lock);
681*7c478bd9Sstevel@tonic-gate 	for (sip = rfs4_cur_servinst; sip != NULL; sip = sip->prev) {
682*7c478bd9Sstevel@tonic-gate 		if (rfs4_servinst_grace_new(sip))
683*7c478bd9Sstevel@tonic-gate 			rfs4_grace_start(sip);
684*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
685*7c478bd9Sstevel@tonic-gate 		n++;
686*7c478bd9Sstevel@tonic-gate #endif
687*7c478bd9Sstevel@tonic-gate 	}
688*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_servinst_lock);
689*7c478bd9Sstevel@tonic-gate 
690*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
691*7c478bd9Sstevel@tonic-gate 	    "rfs4_grace_start_new: started %d new instances", n));
692*7c478bd9Sstevel@tonic-gate }
693*7c478bd9Sstevel@tonic-gate 
694*7c478bd9Sstevel@tonic-gate /*
695*7c478bd9Sstevel@tonic-gate  * Create a new server instance, and make it the currently active instance.
696*7c478bd9Sstevel@tonic-gate  * Note that starting the grace period too early will reduce the clients'
697*7c478bd9Sstevel@tonic-gate  * recovery window.
698*7c478bd9Sstevel@tonic-gate  */
699*7c478bd9Sstevel@tonic-gate void
700*7c478bd9Sstevel@tonic-gate rfs4_servinst_create(int start_grace)
701*7c478bd9Sstevel@tonic-gate {
702*7c478bd9Sstevel@tonic-gate 	rfs4_servinst_t *sip;
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate 	sip = kmem_alloc(sizeof (rfs4_servinst_t), KM_SLEEP);
705*7c478bd9Sstevel@tonic-gate 	rw_init(&sip->rwlock, NULL, RW_DEFAULT, NULL);
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate 	sip->start_time = (time_t)0;
708*7c478bd9Sstevel@tonic-gate 	sip->grace_period = (time_t)0;
709*7c478bd9Sstevel@tonic-gate 	sip->next = NULL;
710*7c478bd9Sstevel@tonic-gate 	sip->prev = NULL;
711*7c478bd9Sstevel@tonic-gate 
712*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_servinst_lock);
713*7c478bd9Sstevel@tonic-gate 	if (rfs4_cur_servinst == NULL) {
714*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
715*7c478bd9Sstevel@tonic-gate 		    "rfs4_servinst_create: creating first instance"));
716*7c478bd9Sstevel@tonic-gate 	} else {
717*7c478bd9Sstevel@tonic-gate 		/* add to linked list */
718*7c478bd9Sstevel@tonic-gate 		sip->prev = rfs4_cur_servinst;
719*7c478bd9Sstevel@tonic-gate 		rfs4_cur_servinst->next = sip;
720*7c478bd9Sstevel@tonic-gate 	}
721*7c478bd9Sstevel@tonic-gate 	if (start_grace)
722*7c478bd9Sstevel@tonic-gate 		rfs4_grace_start(sip);
723*7c478bd9Sstevel@tonic-gate 	/* make the new instance "current" */
724*7c478bd9Sstevel@tonic-gate 	rfs4_cur_servinst = sip;
725*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_servinst_lock);
726*7c478bd9Sstevel@tonic-gate 
727*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
728*7c478bd9Sstevel@tonic-gate 	    "rfs4_servinst_create: new current instance: %p; start_grace: %d",
729*7c478bd9Sstevel@tonic-gate 	    (void *)sip, start_grace));
730*7c478bd9Sstevel@tonic-gate }
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate /*
733*7c478bd9Sstevel@tonic-gate  * In future, we might add a rfs4_servinst_destroy(sip) but, for now, destroy
734*7c478bd9Sstevel@tonic-gate  * all instances directly.
735*7c478bd9Sstevel@tonic-gate  */
736*7c478bd9Sstevel@tonic-gate void
737*7c478bd9Sstevel@tonic-gate rfs4_servinst_destroy_all(void)
738*7c478bd9Sstevel@tonic-gate {
739*7c478bd9Sstevel@tonic-gate 	rfs4_servinst_t *sip, *prev, *current;
740*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
741*7c478bd9Sstevel@tonic-gate 	int n = 0;
742*7c478bd9Sstevel@tonic-gate #endif
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_servinst_lock);
745*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_cur_servinst != NULL);
746*7c478bd9Sstevel@tonic-gate 	current = rfs4_cur_servinst;
747*7c478bd9Sstevel@tonic-gate 	rfs4_cur_servinst = NULL;
748*7c478bd9Sstevel@tonic-gate 	for (sip = current; sip != NULL; sip = prev) {
749*7c478bd9Sstevel@tonic-gate 		prev = sip->prev;
750*7c478bd9Sstevel@tonic-gate 		rw_destroy(&sip->rwlock);
751*7c478bd9Sstevel@tonic-gate 		kmem_free(sip, sizeof (rfs4_servinst_t));
752*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
753*7c478bd9Sstevel@tonic-gate 		n++;
754*7c478bd9Sstevel@tonic-gate #endif
755*7c478bd9Sstevel@tonic-gate 	}
756*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_servinst_lock);
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
759*7c478bd9Sstevel@tonic-gate 	    "rfs4_servinst_destroy_all: destroyed %d instances", n));
760*7c478bd9Sstevel@tonic-gate }
761*7c478bd9Sstevel@tonic-gate 
762*7c478bd9Sstevel@tonic-gate /*
763*7c478bd9Sstevel@tonic-gate  * Assign the current server instance to a client_t.
764*7c478bd9Sstevel@tonic-gate  * Should be called with cp->dbe held.
765*7c478bd9Sstevel@tonic-gate  */
766*7c478bd9Sstevel@tonic-gate void
767*7c478bd9Sstevel@tonic-gate rfs4_servinst_assign(rfs4_client_t *cp, rfs4_servinst_t *sip)
768*7c478bd9Sstevel@tonic-gate {
769*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_refcnt(cp->dbe) > 0);
770*7c478bd9Sstevel@tonic-gate 
771*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_servinst_debug, (CE_NOTE,
772*7c478bd9Sstevel@tonic-gate 	    "rfs4_servinst_assign: client: %p, old: %p, new: %p", (void *)cp,
773*7c478bd9Sstevel@tonic-gate 	    (void *)cp->server_instance, (void *)sip));
774*7c478bd9Sstevel@tonic-gate 
775*7c478bd9Sstevel@tonic-gate 	/*
776*7c478bd9Sstevel@tonic-gate 	 * The lock ensures that if the current instance is in the process
777*7c478bd9Sstevel@tonic-gate 	 * of changing, we will see the new one.
778*7c478bd9Sstevel@tonic-gate 	 */
779*7c478bd9Sstevel@tonic-gate 	mutex_enter(&rfs4_servinst_lock);
780*7c478bd9Sstevel@tonic-gate 	cp->server_instance = sip;
781*7c478bd9Sstevel@tonic-gate 	mutex_exit(&rfs4_servinst_lock);
782*7c478bd9Sstevel@tonic-gate }
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate rfs4_servinst_t *
785*7c478bd9Sstevel@tonic-gate rfs4_servinst(rfs4_client_t *cp)
786*7c478bd9Sstevel@tonic-gate {
787*7c478bd9Sstevel@tonic-gate 	ASSERT(rfs4_dbe_refcnt(cp->dbe) > 0);
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate 	return (cp->server_instance);
790*7c478bd9Sstevel@tonic-gate }
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
793*7c478bd9Sstevel@tonic-gate static void
794*7c478bd9Sstevel@tonic-gate nullfree(caddr_t resop)
795*7c478bd9Sstevel@tonic-gate {
796*7c478bd9Sstevel@tonic-gate }
797*7c478bd9Sstevel@tonic-gate 
798*7c478bd9Sstevel@tonic-gate /*
799*7c478bd9Sstevel@tonic-gate  * This is a fall-through for invalid or not implemented (yet) ops
800*7c478bd9Sstevel@tonic-gate  */
801*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
802*7c478bd9Sstevel@tonic-gate static void
803*7c478bd9Sstevel@tonic-gate rfs4_op_inval(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
804*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
805*7c478bd9Sstevel@tonic-gate {
806*7c478bd9Sstevel@tonic-gate 	*cs->statusp = *((nfsstat4 *)&(resop)->nfs_resop4_u) = NFS4ERR_INVAL;
807*7c478bd9Sstevel@tonic-gate }
808*7c478bd9Sstevel@tonic-gate 
809*7c478bd9Sstevel@tonic-gate /*
810*7c478bd9Sstevel@tonic-gate  * Check if the security flavor, nfsnum, is in the flavor_list.
811*7c478bd9Sstevel@tonic-gate  */
812*7c478bd9Sstevel@tonic-gate bool_t
813*7c478bd9Sstevel@tonic-gate in_flavor_list(int nfsnum, int *flavor_list, int count)
814*7c478bd9Sstevel@tonic-gate {
815*7c478bd9Sstevel@tonic-gate 	int i;
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
818*7c478bd9Sstevel@tonic-gate 		if (nfsnum == flavor_list[i])
819*7c478bd9Sstevel@tonic-gate 			return (TRUE);
820*7c478bd9Sstevel@tonic-gate 	}
821*7c478bd9Sstevel@tonic-gate 	return (FALSE);
822*7c478bd9Sstevel@tonic-gate }
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate /*
825*7c478bd9Sstevel@tonic-gate  * Used by rfs4_op_secinfo to get the security information from the
826*7c478bd9Sstevel@tonic-gate  * export structure associated with the component.
827*7c478bd9Sstevel@tonic-gate  */
828*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
829*7c478bd9Sstevel@tonic-gate static nfsstat4
830*7c478bd9Sstevel@tonic-gate do_rfs4_op_secinfo(struct compound_state *cs, char *nm, SECINFO4res *resp)
831*7c478bd9Sstevel@tonic-gate {
832*7c478bd9Sstevel@tonic-gate 	int error, different_export = 0;
833*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp, *vp, *tvp;
834*7c478bd9Sstevel@tonic-gate 	struct exportinfo *exi = NULL;
835*7c478bd9Sstevel@tonic-gate 	fid_t fid;
836*7c478bd9Sstevel@tonic-gate 	uint_t count, i;
837*7c478bd9Sstevel@tonic-gate 	secinfo4 *resok_val;
838*7c478bd9Sstevel@tonic-gate 	struct secinfo *secp;
839*7c478bd9Sstevel@tonic-gate 	bool_t did_traverse;
840*7c478bd9Sstevel@tonic-gate 	int dotdot, walk;
841*7c478bd9Sstevel@tonic-gate 
842*7c478bd9Sstevel@tonic-gate 	dvp = cs->vp;
843*7c478bd9Sstevel@tonic-gate 	dotdot = (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0');
844*7c478bd9Sstevel@tonic-gate 
845*7c478bd9Sstevel@tonic-gate 	/*
846*7c478bd9Sstevel@tonic-gate 	 * If dotdotting, then need to check whether it's above the
847*7c478bd9Sstevel@tonic-gate 	 * root of a filesystem, or above an export point.
848*7c478bd9Sstevel@tonic-gate 	 */
849*7c478bd9Sstevel@tonic-gate 	if (dotdot) {
850*7c478bd9Sstevel@tonic-gate 
851*7c478bd9Sstevel@tonic-gate 		/*
852*7c478bd9Sstevel@tonic-gate 		 * If dotdotting at the root of a filesystem, then
853*7c478bd9Sstevel@tonic-gate 		 * need to traverse back to the mounted-on filesystem
854*7c478bd9Sstevel@tonic-gate 		 * and do the dotdot lookup there.
855*7c478bd9Sstevel@tonic-gate 		 */
856*7c478bd9Sstevel@tonic-gate 		if (cs->vp->v_flag & VROOT) {
857*7c478bd9Sstevel@tonic-gate 
858*7c478bd9Sstevel@tonic-gate 			/*
859*7c478bd9Sstevel@tonic-gate 			 * If at the system root, then can
860*7c478bd9Sstevel@tonic-gate 			 * go up no further.
861*7c478bd9Sstevel@tonic-gate 			 */
862*7c478bd9Sstevel@tonic-gate 			if (VN_CMP(dvp, rootdir))
863*7c478bd9Sstevel@tonic-gate 				return (puterrno4(ENOENT));
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate 			/*
866*7c478bd9Sstevel@tonic-gate 			 * Traverse back to the mounted-on filesystem
867*7c478bd9Sstevel@tonic-gate 			 */
868*7c478bd9Sstevel@tonic-gate 			dvp = untraverse(cs->vp);
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate 			/*
871*7c478bd9Sstevel@tonic-gate 			 * Set the different_export flag so we remember
872*7c478bd9Sstevel@tonic-gate 			 * to pick up a new exportinfo entry for
873*7c478bd9Sstevel@tonic-gate 			 * this new filesystem.
874*7c478bd9Sstevel@tonic-gate 			 */
875*7c478bd9Sstevel@tonic-gate 			different_export = 1;
876*7c478bd9Sstevel@tonic-gate 		} else {
877*7c478bd9Sstevel@tonic-gate 
878*7c478bd9Sstevel@tonic-gate 			/*
879*7c478bd9Sstevel@tonic-gate 			 * If dotdotting above an export point then set
880*7c478bd9Sstevel@tonic-gate 			 * the different_export to get new export info.
881*7c478bd9Sstevel@tonic-gate 			 */
882*7c478bd9Sstevel@tonic-gate 			different_export = nfs_exported(cs->exi, cs->vp);
883*7c478bd9Sstevel@tonic-gate 		}
884*7c478bd9Sstevel@tonic-gate 	}
885*7c478bd9Sstevel@tonic-gate 
886*7c478bd9Sstevel@tonic-gate 	/*
887*7c478bd9Sstevel@tonic-gate 	 * Get the vnode for the component "nm".
888*7c478bd9Sstevel@tonic-gate 	 */
889*7c478bd9Sstevel@tonic-gate 	error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cs->cr);
890*7c478bd9Sstevel@tonic-gate 	if (error)
891*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
892*7c478bd9Sstevel@tonic-gate 
893*7c478bd9Sstevel@tonic-gate 	VN_SETPATH(rootdir, dvp, vp, nm, strlen(nm));
894*7c478bd9Sstevel@tonic-gate 
895*7c478bd9Sstevel@tonic-gate 	/*
896*7c478bd9Sstevel@tonic-gate 	 * If the vnode is in a pseudo filesystem, or if the security flavor
897*7c478bd9Sstevel@tonic-gate 	 * used in the request is valid but not an explicitly shared flavor,
898*7c478bd9Sstevel@tonic-gate 	 * or the access bit indicates that this is a limited access,
899*7c478bd9Sstevel@tonic-gate 	 * check whether this vnode is visible.
900*7c478bd9Sstevel@tonic-gate 	 */
901*7c478bd9Sstevel@tonic-gate 	if (!different_export &&
902*7c478bd9Sstevel@tonic-gate 	    (PSEUDO(cs->exi) || ! is_exported_sec(cs->nfsflavor, cs->exi) ||
903*7c478bd9Sstevel@tonic-gate 	    cs->access & CS_ACCESS_LIMITED)) {
904*7c478bd9Sstevel@tonic-gate 		if (! nfs_visible(cs->exi, vp, &different_export)) {
905*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
906*7c478bd9Sstevel@tonic-gate 			return (puterrno4(ENOENT));
907*7c478bd9Sstevel@tonic-gate 		}
908*7c478bd9Sstevel@tonic-gate 	}
909*7c478bd9Sstevel@tonic-gate 
910*7c478bd9Sstevel@tonic-gate 	/*
911*7c478bd9Sstevel@tonic-gate 	 * If it's a mountpoint, then traverse it.
912*7c478bd9Sstevel@tonic-gate 	 */
913*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(vp)) {
914*7c478bd9Sstevel@tonic-gate 		tvp = vp;
915*7c478bd9Sstevel@tonic-gate 		if ((error = traverse(&tvp)) != 0) {
916*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
917*7c478bd9Sstevel@tonic-gate 			return (puterrno4(error));
918*7c478bd9Sstevel@tonic-gate 		}
919*7c478bd9Sstevel@tonic-gate 		/* remember that we had to traverse mountpoint */
920*7c478bd9Sstevel@tonic-gate 		did_traverse = TRUE;
921*7c478bd9Sstevel@tonic-gate 		vp = tvp;
922*7c478bd9Sstevel@tonic-gate 		different_export = 1;
923*7c478bd9Sstevel@tonic-gate 	} else if (vp->v_vfsp != dvp->v_vfsp) {
924*7c478bd9Sstevel@tonic-gate 		/*
925*7c478bd9Sstevel@tonic-gate 		 * If vp isn't a mountpoint and the vfs ptrs aren't the same,
926*7c478bd9Sstevel@tonic-gate 		 * then vp is probably an LOFS object.  We don't need the
927*7c478bd9Sstevel@tonic-gate 		 * realvp, we just need to know that we might have crossed
928*7c478bd9Sstevel@tonic-gate 		 * a server fs boundary and need to call checkexport4.
929*7c478bd9Sstevel@tonic-gate 		 * (LOFS lookup hides server fs mountpoints, and actually calls
930*7c478bd9Sstevel@tonic-gate 		 * traverse)
931*7c478bd9Sstevel@tonic-gate 		 */
932*7c478bd9Sstevel@tonic-gate 		different_export = 1;
933*7c478bd9Sstevel@tonic-gate 		did_traverse = FALSE;
934*7c478bd9Sstevel@tonic-gate 	}
935*7c478bd9Sstevel@tonic-gate 
936*7c478bd9Sstevel@tonic-gate 	/*
937*7c478bd9Sstevel@tonic-gate 	 * Get the export information for it.
938*7c478bd9Sstevel@tonic-gate 	 */
939*7c478bd9Sstevel@tonic-gate 	if (different_export) {
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate 		bzero(&fid, sizeof (fid));
942*7c478bd9Sstevel@tonic-gate 		fid.fid_len = MAXFIDSZ;
943*7c478bd9Sstevel@tonic-gate 		error = vop_fid_pseudo(vp, &fid);
944*7c478bd9Sstevel@tonic-gate 		if (error) {
945*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
946*7c478bd9Sstevel@tonic-gate 			return (puterrno4(error));
947*7c478bd9Sstevel@tonic-gate 		}
948*7c478bd9Sstevel@tonic-gate 
949*7c478bd9Sstevel@tonic-gate 		if (dotdot)
950*7c478bd9Sstevel@tonic-gate 			exi = nfs_vptoexi(NULL, vp, cs->cr, &walk, NULL, TRUE);
951*7c478bd9Sstevel@tonic-gate 		else
952*7c478bd9Sstevel@tonic-gate 			exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp);
953*7c478bd9Sstevel@tonic-gate 
954*7c478bd9Sstevel@tonic-gate 		if (exi == NULL) {
955*7c478bd9Sstevel@tonic-gate 			if (did_traverse == TRUE) {
956*7c478bd9Sstevel@tonic-gate 				/*
957*7c478bd9Sstevel@tonic-gate 				 * If this vnode is a mounted-on vnode,
958*7c478bd9Sstevel@tonic-gate 				 * but the mounted-on file system is not
959*7c478bd9Sstevel@tonic-gate 				 * exported, send back the secinfo for
960*7c478bd9Sstevel@tonic-gate 				 * the exported node that the mounted-on
961*7c478bd9Sstevel@tonic-gate 				 * vnode lives in.
962*7c478bd9Sstevel@tonic-gate 				 */
963*7c478bd9Sstevel@tonic-gate 				exi = cs->exi;
964*7c478bd9Sstevel@tonic-gate 			} else {
965*7c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
966*7c478bd9Sstevel@tonic-gate 				return (puterrno4(EACCES));
967*7c478bd9Sstevel@tonic-gate 			}
968*7c478bd9Sstevel@tonic-gate 		}
969*7c478bd9Sstevel@tonic-gate 	} else {
970*7c478bd9Sstevel@tonic-gate 		exi = cs->exi;
971*7c478bd9Sstevel@tonic-gate 	}
972*7c478bd9Sstevel@tonic-gate 	ASSERT(exi != NULL);
973*7c478bd9Sstevel@tonic-gate 
974*7c478bd9Sstevel@tonic-gate 
975*7c478bd9Sstevel@tonic-gate 	/*
976*7c478bd9Sstevel@tonic-gate 	 * Create the secinfo result based on the security information
977*7c478bd9Sstevel@tonic-gate 	 * from the exportinfo structure (exi).
978*7c478bd9Sstevel@tonic-gate 	 *
979*7c478bd9Sstevel@tonic-gate 	 * Return all flavors for a pseudo node.
980*7c478bd9Sstevel@tonic-gate 	 * For a real export node, return the flavor that the client
981*7c478bd9Sstevel@tonic-gate 	 * has access with.
982*7c478bd9Sstevel@tonic-gate 	 */
983*7c478bd9Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&exported_lock));
984*7c478bd9Sstevel@tonic-gate 	if (PSEUDO(exi)) {
985*7c478bd9Sstevel@tonic-gate 		count = exi->exi_export.ex_seccnt; /* total sec count */
986*7c478bd9Sstevel@tonic-gate 		resok_val = kmem_alloc(count * sizeof (secinfo4), KM_SLEEP);
987*7c478bd9Sstevel@tonic-gate 		secp = exi->exi_export.ex_secinfo;
988*7c478bd9Sstevel@tonic-gate 
989*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < count; i++) {
990*7c478bd9Sstevel@tonic-gate 		    resok_val[i].flavor = secp[i].s_secinfo.sc_rpcnum;
991*7c478bd9Sstevel@tonic-gate 		    if (resok_val[i].flavor == RPCSEC_GSS) {
992*7c478bd9Sstevel@tonic-gate 			rpcsec_gss_info *info;
993*7c478bd9Sstevel@tonic-gate 
994*7c478bd9Sstevel@tonic-gate 			info = &resok_val[i].flavor_info;
995*7c478bd9Sstevel@tonic-gate 			info->qop = secp[i].s_secinfo.sc_qop;
996*7c478bd9Sstevel@tonic-gate 			info->service =
997*7c478bd9Sstevel@tonic-gate 				(rpc_gss_svc_t)secp[i].s_secinfo.sc_service;
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 			/* get oid opaque data */
1000*7c478bd9Sstevel@tonic-gate 			info->oid.sec_oid4_len =
1001*7c478bd9Sstevel@tonic-gate 				secp[i].s_secinfo.sc_gss_mech_type->length;
1002*7c478bd9Sstevel@tonic-gate 			info->oid.sec_oid4_val =
1003*7c478bd9Sstevel@tonic-gate 				kmem_alloc(
1004*7c478bd9Sstevel@tonic-gate 				    secp[i].s_secinfo.sc_gss_mech_type->length,
1005*7c478bd9Sstevel@tonic-gate 				    KM_SLEEP);
1006*7c478bd9Sstevel@tonic-gate 			bcopy(secp[i].s_secinfo.sc_gss_mech_type->elements,
1007*7c478bd9Sstevel@tonic-gate 				info->oid.sec_oid4_val, info->oid.sec_oid4_len);
1008*7c478bd9Sstevel@tonic-gate 		    }
1009*7c478bd9Sstevel@tonic-gate 		}
1010*7c478bd9Sstevel@tonic-gate 		resp->SECINFO4resok_len = count;
1011*7c478bd9Sstevel@tonic-gate 		resp->SECINFO4resok_val = resok_val;
1012*7c478bd9Sstevel@tonic-gate 	} else {
1013*7c478bd9Sstevel@tonic-gate 		int ret_cnt = 0, k = 0;
1014*7c478bd9Sstevel@tonic-gate 		int *flavor_list;
1015*7c478bd9Sstevel@tonic-gate 
1016*7c478bd9Sstevel@tonic-gate 		count = exi->exi_export.ex_seccnt; /* total sec count */
1017*7c478bd9Sstevel@tonic-gate 		secp = exi->exi_export.ex_secinfo;
1018*7c478bd9Sstevel@tonic-gate 
1019*7c478bd9Sstevel@tonic-gate 		flavor_list = kmem_alloc(count * sizeof (int), KM_SLEEP);
1020*7c478bd9Sstevel@tonic-gate 		/* find out which flavors to return */
1021*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < count; i ++) {
1022*7c478bd9Sstevel@tonic-gate 			int access, flavor, perm;
1023*7c478bd9Sstevel@tonic-gate 
1024*7c478bd9Sstevel@tonic-gate 			flavor = secp[i].s_secinfo.sc_nfsnum;
1025*7c478bd9Sstevel@tonic-gate 			perm = secp[i].s_flags;
1026*7c478bd9Sstevel@tonic-gate 
1027*7c478bd9Sstevel@tonic-gate 			access = nfsauth4_secinfo_access(exi, cs->req,
1028*7c478bd9Sstevel@tonic-gate 						flavor, perm);
1029*7c478bd9Sstevel@tonic-gate 
1030*7c478bd9Sstevel@tonic-gate 			if (! (access & NFSAUTH_DENIED) &&
1031*7c478bd9Sstevel@tonic-gate 			    ! (access & NFSAUTH_WRONGSEC)) {
1032*7c478bd9Sstevel@tonic-gate 				flavor_list[ret_cnt] = flavor;
1033*7c478bd9Sstevel@tonic-gate 				ret_cnt++;
1034*7c478bd9Sstevel@tonic-gate 			}
1035*7c478bd9Sstevel@tonic-gate 		}
1036*7c478bd9Sstevel@tonic-gate 
1037*7c478bd9Sstevel@tonic-gate 		/* Create the returning SECINFO value */
1038*7c478bd9Sstevel@tonic-gate 		resok_val = kmem_alloc(ret_cnt * sizeof (secinfo4), KM_SLEEP);
1039*7c478bd9Sstevel@tonic-gate 
1040*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < count; i++) {
1041*7c478bd9Sstevel@tonic-gate 		/* If the flavor is in the flavor list, fill in resok_val. */
1042*7c478bd9Sstevel@tonic-gate 		    if (in_flavor_list(secp[i].s_secinfo.sc_nfsnum,
1043*7c478bd9Sstevel@tonic-gate 						flavor_list, ret_cnt)) {
1044*7c478bd9Sstevel@tonic-gate 			resok_val[k].flavor = secp[i].s_secinfo.sc_rpcnum;
1045*7c478bd9Sstevel@tonic-gate 			if (resok_val[k].flavor == RPCSEC_GSS) {
1046*7c478bd9Sstevel@tonic-gate 			    rpcsec_gss_info *info;
1047*7c478bd9Sstevel@tonic-gate 
1048*7c478bd9Sstevel@tonic-gate 			    info = &resok_val[k].flavor_info;
1049*7c478bd9Sstevel@tonic-gate 			    info->qop = secp[i].s_secinfo.sc_qop;
1050*7c478bd9Sstevel@tonic-gate 			    info->service =
1051*7c478bd9Sstevel@tonic-gate 				(rpc_gss_svc_t)secp[i].s_secinfo.sc_service;
1052*7c478bd9Sstevel@tonic-gate 
1053*7c478bd9Sstevel@tonic-gate 			    /* get oid opaque data */
1054*7c478bd9Sstevel@tonic-gate 			    info->oid.sec_oid4_len =
1055*7c478bd9Sstevel@tonic-gate 				secp[i].s_secinfo.sc_gss_mech_type->length;
1056*7c478bd9Sstevel@tonic-gate 			    info->oid.sec_oid4_val =
1057*7c478bd9Sstevel@tonic-gate 				kmem_alloc(
1058*7c478bd9Sstevel@tonic-gate 				    secp[i].s_secinfo.sc_gss_mech_type->length,
1059*7c478bd9Sstevel@tonic-gate 				    KM_SLEEP);
1060*7c478bd9Sstevel@tonic-gate 			    bcopy(secp[i].s_secinfo.sc_gss_mech_type->elements,
1061*7c478bd9Sstevel@tonic-gate 				info->oid.sec_oid4_val, info->oid.sec_oid4_len);
1062*7c478bd9Sstevel@tonic-gate 			}
1063*7c478bd9Sstevel@tonic-gate 			k++;
1064*7c478bd9Sstevel@tonic-gate 		    }
1065*7c478bd9Sstevel@tonic-gate 		    if (k >= ret_cnt)
1066*7c478bd9Sstevel@tonic-gate 			break;
1067*7c478bd9Sstevel@tonic-gate 		}
1068*7c478bd9Sstevel@tonic-gate 		resp->SECINFO4resok_len = ret_cnt;
1069*7c478bd9Sstevel@tonic-gate 		resp->SECINFO4resok_val = resok_val;
1070*7c478bd9Sstevel@tonic-gate 		kmem_free(flavor_list, count * sizeof (int));
1071*7c478bd9Sstevel@tonic-gate 	}
1072*7c478bd9Sstevel@tonic-gate 
1073*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
1074*7c478bd9Sstevel@tonic-gate 	return (NFS4_OK);
1075*7c478bd9Sstevel@tonic-gate }
1076*7c478bd9Sstevel@tonic-gate 
1077*7c478bd9Sstevel@tonic-gate /*
1078*7c478bd9Sstevel@tonic-gate  * SECINFO (Operation 33): Obtain required security information on
1079*7c478bd9Sstevel@tonic-gate  * the component name in the format of (security-mechanism-oid, qop, service)
1080*7c478bd9Sstevel@tonic-gate  * triplets.
1081*7c478bd9Sstevel@tonic-gate  */
1082*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
1083*7c478bd9Sstevel@tonic-gate static void
1084*7c478bd9Sstevel@tonic-gate rfs4_op_secinfo(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
1085*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
1086*7c478bd9Sstevel@tonic-gate {
1087*7c478bd9Sstevel@tonic-gate 	SECINFO4res *resp = &resop->nfs_resop4_u.opsecinfo;
1088*7c478bd9Sstevel@tonic-gate 	utf8string *utfnm = &argop->nfs_argop4_u.opsecinfo.name;
1089*7c478bd9Sstevel@tonic-gate 	uint_t len;
1090*7c478bd9Sstevel@tonic-gate 	char *nm;
1091*7c478bd9Sstevel@tonic-gate 
1092*7c478bd9Sstevel@tonic-gate 	/*
1093*7c478bd9Sstevel@tonic-gate 	 * Current file handle (cfh) should have been set before getting
1094*7c478bd9Sstevel@tonic-gate 	 * into this function. If not, return error.
1095*7c478bd9Sstevel@tonic-gate 	 */
1096*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
1097*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
1098*7c478bd9Sstevel@tonic-gate 		return;
1099*7c478bd9Sstevel@tonic-gate 	}
1100*7c478bd9Sstevel@tonic-gate 
1101*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VDIR) {
1102*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
1103*7c478bd9Sstevel@tonic-gate 		return;
1104*7c478bd9Sstevel@tonic-gate 	}
1105*7c478bd9Sstevel@tonic-gate 
1106*7c478bd9Sstevel@tonic-gate 	/*
1107*7c478bd9Sstevel@tonic-gate 	 * Verify the component name. If failed, error out, but
1108*7c478bd9Sstevel@tonic-gate 	 * do not error out if the component name is a "..".
1109*7c478bd9Sstevel@tonic-gate 	 * SECINFO will return its parents secinfo data for SECINFO "..".
1110*7c478bd9Sstevel@tonic-gate 	 */
1111*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(utfnm)) {
1112*7c478bd9Sstevel@tonic-gate 		if (utfnm->utf8string_len != 2 ||
1113*7c478bd9Sstevel@tonic-gate 				utfnm->utf8string_val[0] != '.' ||
1114*7c478bd9Sstevel@tonic-gate 				utfnm->utf8string_val[1] != '.') {
1115*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_INVAL;
1116*7c478bd9Sstevel@tonic-gate 			return;
1117*7c478bd9Sstevel@tonic-gate 		}
1118*7c478bd9Sstevel@tonic-gate 	}
1119*7c478bd9Sstevel@tonic-gate 
1120*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_str(utfnm, &len, NULL);
1121*7c478bd9Sstevel@tonic-gate 	if (nm == NULL) {
1122*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
1123*7c478bd9Sstevel@tonic-gate 		return;
1124*7c478bd9Sstevel@tonic-gate 	}
1125*7c478bd9Sstevel@tonic-gate 
1126*7c478bd9Sstevel@tonic-gate 	if (len > MAXNAMELEN) {
1127*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
1128*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
1129*7c478bd9Sstevel@tonic-gate 		return;
1130*7c478bd9Sstevel@tonic-gate 	}
1131*7c478bd9Sstevel@tonic-gate 
1132*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = do_rfs4_op_secinfo(cs, nm, resp);
1133*7c478bd9Sstevel@tonic-gate 
1134*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, len);
1135*7c478bd9Sstevel@tonic-gate }
1136*7c478bd9Sstevel@tonic-gate 
1137*7c478bd9Sstevel@tonic-gate /*
1138*7c478bd9Sstevel@tonic-gate  * Free SECINFO result.
1139*7c478bd9Sstevel@tonic-gate  */
1140*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
1141*7c478bd9Sstevel@tonic-gate static void
1142*7c478bd9Sstevel@tonic-gate rfs4_op_secinfo_free(nfs_resop4 *resop)
1143*7c478bd9Sstevel@tonic-gate {
1144*7c478bd9Sstevel@tonic-gate 	SECINFO4res *resp = &resop->nfs_resop4_u.opsecinfo;
1145*7c478bd9Sstevel@tonic-gate 	int count, i;
1146*7c478bd9Sstevel@tonic-gate 	secinfo4 *resok_val;
1147*7c478bd9Sstevel@tonic-gate 
1148*7c478bd9Sstevel@tonic-gate 	/* If this is not an Ok result, nothing to free. */
1149*7c478bd9Sstevel@tonic-gate 	if (resp->status != NFS4_OK) {
1150*7c478bd9Sstevel@tonic-gate 		return;
1151*7c478bd9Sstevel@tonic-gate 	}
1152*7c478bd9Sstevel@tonic-gate 
1153*7c478bd9Sstevel@tonic-gate 	count = resp->SECINFO4resok_len;
1154*7c478bd9Sstevel@tonic-gate 	resok_val = resp->SECINFO4resok_val;
1155*7c478bd9Sstevel@tonic-gate 
1156*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
1157*7c478bd9Sstevel@tonic-gate 	    if (resok_val[i].flavor == RPCSEC_GSS) {
1158*7c478bd9Sstevel@tonic-gate 		rpcsec_gss_info *info;
1159*7c478bd9Sstevel@tonic-gate 
1160*7c478bd9Sstevel@tonic-gate 		info = &resok_val[i].flavor_info;
1161*7c478bd9Sstevel@tonic-gate 		kmem_free(info->oid.sec_oid4_val, info->oid.sec_oid4_len);
1162*7c478bd9Sstevel@tonic-gate 	    }
1163*7c478bd9Sstevel@tonic-gate 	}
1164*7c478bd9Sstevel@tonic-gate 	kmem_free(resok_val, count * sizeof (secinfo4));
1165*7c478bd9Sstevel@tonic-gate 	resp->SECINFO4resok_len = 0;
1166*7c478bd9Sstevel@tonic-gate 	resp->SECINFO4resok_val = NULL;
1167*7c478bd9Sstevel@tonic-gate }
1168*7c478bd9Sstevel@tonic-gate 
1169*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
1170*7c478bd9Sstevel@tonic-gate static void
1171*7c478bd9Sstevel@tonic-gate rfs4_op_access(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
1172*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
1173*7c478bd9Sstevel@tonic-gate {
1174*7c478bd9Sstevel@tonic-gate 	ACCESS4args *args = &argop->nfs_argop4_u.opaccess;
1175*7c478bd9Sstevel@tonic-gate 	ACCESS4res *resp = &resop->nfs_resop4_u.opaccess;
1176*7c478bd9Sstevel@tonic-gate 	int error;
1177*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
1178*7c478bd9Sstevel@tonic-gate 	struct vattr va;
1179*7c478bd9Sstevel@tonic-gate 	int checkwriteperm;
1180*7c478bd9Sstevel@tonic-gate 	cred_t *cr = cs->cr;
1181*7c478bd9Sstevel@tonic-gate 
1182*7c478bd9Sstevel@tonic-gate #if 0	/* XXX allow access even if !cs->access. Eventually only pseudo fs */
1183*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
1184*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
1185*7c478bd9Sstevel@tonic-gate 		return;
1186*7c478bd9Sstevel@tonic-gate 	}
1187*7c478bd9Sstevel@tonic-gate #endif
1188*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
1189*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
1190*7c478bd9Sstevel@tonic-gate 		return;
1191*7c478bd9Sstevel@tonic-gate 	}
1192*7c478bd9Sstevel@tonic-gate 
1193*7c478bd9Sstevel@tonic-gate 	ASSERT(cr != NULL);
1194*7c478bd9Sstevel@tonic-gate 
1195*7c478bd9Sstevel@tonic-gate 	vp = cs->vp;
1196*7c478bd9Sstevel@tonic-gate 
1197*7c478bd9Sstevel@tonic-gate 	/*
1198*7c478bd9Sstevel@tonic-gate 	 * If the file system is exported read only, it is not appropriate
1199*7c478bd9Sstevel@tonic-gate 	 * to check write permissions for regular files and directories.
1200*7c478bd9Sstevel@tonic-gate 	 * Special files are interpreted by the client, so the underlying
1201*7c478bd9Sstevel@tonic-gate 	 * permissions are sent back to the client for interpretation.
1202*7c478bd9Sstevel@tonic-gate 	 */
1203*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req) &&
1204*7c478bd9Sstevel@tonic-gate 		(vp->v_type == VREG || vp->v_type == VDIR))
1205*7c478bd9Sstevel@tonic-gate 		checkwriteperm = 0;
1206*7c478bd9Sstevel@tonic-gate 	else
1207*7c478bd9Sstevel@tonic-gate 		checkwriteperm = 1;
1208*7c478bd9Sstevel@tonic-gate 
1209*7c478bd9Sstevel@tonic-gate 	/*
1210*7c478bd9Sstevel@tonic-gate 	 * XXX
1211*7c478bd9Sstevel@tonic-gate 	 * We need the mode so that we can correctly determine access
1212*7c478bd9Sstevel@tonic-gate 	 * permissions relative to a mandatory lock file.  Access to
1213*7c478bd9Sstevel@tonic-gate 	 * mandatory lock files is denied on the server, so it might
1214*7c478bd9Sstevel@tonic-gate 	 * as well be reflected to the server during the open.
1215*7c478bd9Sstevel@tonic-gate 	 */
1216*7c478bd9Sstevel@tonic-gate 	va.va_mask = AT_MODE;
1217*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(vp, &va, 0, cr);
1218*7c478bd9Sstevel@tonic-gate 	if (error) {
1219*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1220*7c478bd9Sstevel@tonic-gate 		return;
1221*7c478bd9Sstevel@tonic-gate 	}
1222*7c478bd9Sstevel@tonic-gate 
1223*7c478bd9Sstevel@tonic-gate 	resp->access = 0;
1224*7c478bd9Sstevel@tonic-gate 	resp->supported = 0;
1225*7c478bd9Sstevel@tonic-gate 
1226*7c478bd9Sstevel@tonic-gate 	if (args->access & ACCESS4_READ) {
1227*7c478bd9Sstevel@tonic-gate 		error = VOP_ACCESS(vp, VREAD, 0, cr);
1228*7c478bd9Sstevel@tonic-gate 		if (!error && !MANDLOCK(vp, va.va_mode))
1229*7c478bd9Sstevel@tonic-gate 			resp->access |= ACCESS4_READ;
1230*7c478bd9Sstevel@tonic-gate 		resp->supported |= ACCESS4_READ;
1231*7c478bd9Sstevel@tonic-gate 	}
1232*7c478bd9Sstevel@tonic-gate 	if ((args->access & ACCESS4_LOOKUP) && vp->v_type == VDIR) {
1233*7c478bd9Sstevel@tonic-gate 		error = VOP_ACCESS(vp, VEXEC, 0, cr);
1234*7c478bd9Sstevel@tonic-gate 		if (!error)
1235*7c478bd9Sstevel@tonic-gate 			resp->access |= ACCESS4_LOOKUP;
1236*7c478bd9Sstevel@tonic-gate 		resp->supported |= ACCESS4_LOOKUP;
1237*7c478bd9Sstevel@tonic-gate 	}
1238*7c478bd9Sstevel@tonic-gate 	if (checkwriteperm &&
1239*7c478bd9Sstevel@tonic-gate 	    (args->access & (ACCESS4_MODIFY|ACCESS4_EXTEND))) {
1240*7c478bd9Sstevel@tonic-gate 		error = VOP_ACCESS(vp, VWRITE, 0, cr);
1241*7c478bd9Sstevel@tonic-gate 		if (!error && !MANDLOCK(vp, va.va_mode))
1242*7c478bd9Sstevel@tonic-gate 			resp->access |=
1243*7c478bd9Sstevel@tonic-gate 			    (args->access & (ACCESS4_MODIFY|ACCESS4_EXTEND));
1244*7c478bd9Sstevel@tonic-gate 		resp->supported |= (ACCESS4_MODIFY|ACCESS4_EXTEND);
1245*7c478bd9Sstevel@tonic-gate 	}
1246*7c478bd9Sstevel@tonic-gate 
1247*7c478bd9Sstevel@tonic-gate 	if (checkwriteperm &&
1248*7c478bd9Sstevel@tonic-gate 	    (args->access & ACCESS4_DELETE) && vp->v_type == VDIR) {
1249*7c478bd9Sstevel@tonic-gate 		error = VOP_ACCESS(vp, VWRITE, 0, cr);
1250*7c478bd9Sstevel@tonic-gate 		if (!error)
1251*7c478bd9Sstevel@tonic-gate 			resp->access |= ACCESS4_DELETE;
1252*7c478bd9Sstevel@tonic-gate 		resp->supported |= ACCESS4_DELETE;
1253*7c478bd9Sstevel@tonic-gate 	}
1254*7c478bd9Sstevel@tonic-gate 	if (args->access & ACCESS4_EXECUTE && vp->v_type != VDIR) {
1255*7c478bd9Sstevel@tonic-gate 		error = VOP_ACCESS(vp, VEXEC, 0, cr);
1256*7c478bd9Sstevel@tonic-gate 		if (!error && !MANDLOCK(vp, va.va_mode))
1257*7c478bd9Sstevel@tonic-gate 			resp->access |= ACCESS4_EXECUTE;
1258*7c478bd9Sstevel@tonic-gate 		resp->supported |= ACCESS4_EXECUTE;
1259*7c478bd9Sstevel@tonic-gate 	}
1260*7c478bd9Sstevel@tonic-gate 
1261*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
1262*7c478bd9Sstevel@tonic-gate }
1263*7c478bd9Sstevel@tonic-gate 
1264*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
1265*7c478bd9Sstevel@tonic-gate static void
1266*7c478bd9Sstevel@tonic-gate rfs4_op_commit(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
1267*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
1268*7c478bd9Sstevel@tonic-gate {
1269*7c478bd9Sstevel@tonic-gate 	COMMIT4args *args = &argop->nfs_argop4_u.opcommit;
1270*7c478bd9Sstevel@tonic-gate 	COMMIT4res *resp = &resop->nfs_resop4_u.opcommit;
1271*7c478bd9Sstevel@tonic-gate 	int error;
1272*7c478bd9Sstevel@tonic-gate 	vnode_t *vp = cs->vp;
1273*7c478bd9Sstevel@tonic-gate 	cred_t *cr = cs->cr;
1274*7c478bd9Sstevel@tonic-gate 	vattr_t va;
1275*7c478bd9Sstevel@tonic-gate 
1276*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
1277*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
1278*7c478bd9Sstevel@tonic-gate 		return;
1279*7c478bd9Sstevel@tonic-gate 	}
1280*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
1281*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
1282*7c478bd9Sstevel@tonic-gate 		return;
1283*7c478bd9Sstevel@tonic-gate 	}
1284*7c478bd9Sstevel@tonic-gate 
1285*7c478bd9Sstevel@tonic-gate 	if (args->offset + args->count < args->offset) {
1286*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
1287*7c478bd9Sstevel@tonic-gate 		return;
1288*7c478bd9Sstevel@tonic-gate 	}
1289*7c478bd9Sstevel@tonic-gate 
1290*7c478bd9Sstevel@tonic-gate 	va.va_mask = AT_UID;
1291*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(vp, &va, 0, cr);
1292*7c478bd9Sstevel@tonic-gate 
1293*7c478bd9Sstevel@tonic-gate 	/*
1294*7c478bd9Sstevel@tonic-gate 	 * If we can't get the attributes, then we can't do the
1295*7c478bd9Sstevel@tonic-gate 	 * right access checking.  So, we'll fail the request.
1296*7c478bd9Sstevel@tonic-gate 	 */
1297*7c478bd9Sstevel@tonic-gate 	if (error) {
1298*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1299*7c478bd9Sstevel@tonic-gate 		return;
1300*7c478bd9Sstevel@tonic-gate 	}
1301*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
1302*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
1303*7c478bd9Sstevel@tonic-gate 		return;
1304*7c478bd9Sstevel@tonic-gate 	}
1305*7c478bd9Sstevel@tonic-gate 
1306*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VREG) {
1307*7c478bd9Sstevel@tonic-gate 		if (vp->v_type == VDIR)
1308*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_ISDIR;
1309*7c478bd9Sstevel@tonic-gate 		else
1310*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_INVAL;
1311*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status;
1312*7c478bd9Sstevel@tonic-gate 		return;
1313*7c478bd9Sstevel@tonic-gate 	}
1314*7c478bd9Sstevel@tonic-gate 
1315*7c478bd9Sstevel@tonic-gate 	if (crgetuid(cr) != va.va_uid &&
1316*7c478bd9Sstevel@tonic-gate 	    (error = VOP_ACCESS(vp, VWRITE, 0, cs->cr))) {
1317*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1318*7c478bd9Sstevel@tonic-gate 		return;
1319*7c478bd9Sstevel@tonic-gate 	}
1320*7c478bd9Sstevel@tonic-gate 
1321*7c478bd9Sstevel@tonic-gate 	error = VOP_PUTPAGE(vp, args->offset, args->count, 0, cr);
1322*7c478bd9Sstevel@tonic-gate 	if (!error)
1323*7c478bd9Sstevel@tonic-gate 		error = VOP_FSYNC(vp, FNODSYNC, cr);
1324*7c478bd9Sstevel@tonic-gate 
1325*7c478bd9Sstevel@tonic-gate 	if (error) {
1326*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1327*7c478bd9Sstevel@tonic-gate 		return;
1328*7c478bd9Sstevel@tonic-gate 	}
1329*7c478bd9Sstevel@tonic-gate 
1330*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
1331*7c478bd9Sstevel@tonic-gate 	resp->writeverf = Write4verf;
1332*7c478bd9Sstevel@tonic-gate }
1333*7c478bd9Sstevel@tonic-gate 
1334*7c478bd9Sstevel@tonic-gate /*
1335*7c478bd9Sstevel@tonic-gate  * rfs4_op_mknod is called from rfs4_op_create after all initial verification
1336*7c478bd9Sstevel@tonic-gate  * was completed. It does the nfsv4 create for special files.
1337*7c478bd9Sstevel@tonic-gate  */
1338*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
1339*7c478bd9Sstevel@tonic-gate static vnode_t *
1340*7c478bd9Sstevel@tonic-gate do_rfs4_op_mknod(CREATE4args *args, CREATE4res *resp, struct svc_req *req,
1341*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs, vattr_t *vap, char *nm)
1342*7c478bd9Sstevel@tonic-gate {
1343*7c478bd9Sstevel@tonic-gate 	int error;
1344*7c478bd9Sstevel@tonic-gate 	cred_t *cr = cs->cr;
1345*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp = cs->vp;
1346*7c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL;
1347*7c478bd9Sstevel@tonic-gate 	int mode;
1348*7c478bd9Sstevel@tonic-gate 	enum vcexcl excl;
1349*7c478bd9Sstevel@tonic-gate 
1350*7c478bd9Sstevel@tonic-gate 	switch (args->type) {
1351*7c478bd9Sstevel@tonic-gate 	case NF4CHR:
1352*7c478bd9Sstevel@tonic-gate 	case NF4BLK:
1353*7c478bd9Sstevel@tonic-gate 		if (secpolicy_sys_devices(cr) != 0) {
1354*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_PERM;
1355*7c478bd9Sstevel@tonic-gate 			return (NULL);
1356*7c478bd9Sstevel@tonic-gate 		}
1357*7c478bd9Sstevel@tonic-gate 		if (args->type == NF4CHR)
1358*7c478bd9Sstevel@tonic-gate 			vap->va_type = VCHR;
1359*7c478bd9Sstevel@tonic-gate 		else
1360*7c478bd9Sstevel@tonic-gate 			vap->va_type = VBLK;
1361*7c478bd9Sstevel@tonic-gate 		vap->va_rdev = makedevice(args->ftype4_u.devdata.specdata1,
1362*7c478bd9Sstevel@tonic-gate 					args->ftype4_u.devdata.specdata2);
1363*7c478bd9Sstevel@tonic-gate 		vap->va_mask |= AT_RDEV;
1364*7c478bd9Sstevel@tonic-gate 		break;
1365*7c478bd9Sstevel@tonic-gate 	case NF4SOCK:
1366*7c478bd9Sstevel@tonic-gate 		vap->va_type = VSOCK;
1367*7c478bd9Sstevel@tonic-gate 		break;
1368*7c478bd9Sstevel@tonic-gate 	case NF4FIFO:
1369*7c478bd9Sstevel@tonic-gate 		vap->va_type = VFIFO;
1370*7c478bd9Sstevel@tonic-gate 		break;
1371*7c478bd9Sstevel@tonic-gate 	default:
1372*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADTYPE;
1373*7c478bd9Sstevel@tonic-gate 		return (NULL);
1374*7c478bd9Sstevel@tonic-gate 	}
1375*7c478bd9Sstevel@tonic-gate 
1376*7c478bd9Sstevel@tonic-gate 	/*
1377*7c478bd9Sstevel@tonic-gate 	 * Must specify the mode.
1378*7c478bd9Sstevel@tonic-gate 	 */
1379*7c478bd9Sstevel@tonic-gate 	if (!(vap->va_mask & AT_MODE)) {
1380*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
1381*7c478bd9Sstevel@tonic-gate 		return (NULL);
1382*7c478bd9Sstevel@tonic-gate 	}
1383*7c478bd9Sstevel@tonic-gate 
1384*7c478bd9Sstevel@tonic-gate 	excl = EXCL;
1385*7c478bd9Sstevel@tonic-gate 
1386*7c478bd9Sstevel@tonic-gate 	mode = 0;
1387*7c478bd9Sstevel@tonic-gate 
1388*7c478bd9Sstevel@tonic-gate 	error = VOP_CREATE(dvp, nm, vap, excl, mode, &vp, cr, 0);
1389*7c478bd9Sstevel@tonic-gate 	if (error) {
1390*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1391*7c478bd9Sstevel@tonic-gate 		return (NULL);
1392*7c478bd9Sstevel@tonic-gate 	}
1393*7c478bd9Sstevel@tonic-gate 	return (vp);
1394*7c478bd9Sstevel@tonic-gate }
1395*7c478bd9Sstevel@tonic-gate 
1396*7c478bd9Sstevel@tonic-gate /*
1397*7c478bd9Sstevel@tonic-gate  * nfsv4 create is used to create non-regular files. For regular files,
1398*7c478bd9Sstevel@tonic-gate  * use nfsv4 open.
1399*7c478bd9Sstevel@tonic-gate  */
1400*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
1401*7c478bd9Sstevel@tonic-gate static void
1402*7c478bd9Sstevel@tonic-gate rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
1403*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
1404*7c478bd9Sstevel@tonic-gate {
1405*7c478bd9Sstevel@tonic-gate 	CREATE4args *args = &argop->nfs_argop4_u.opcreate;
1406*7c478bd9Sstevel@tonic-gate 	CREATE4res *resp = &resop->nfs_resop4_u.opcreate;
1407*7c478bd9Sstevel@tonic-gate 	int error;
1408*7c478bd9Sstevel@tonic-gate 	struct vattr bva, iva, iva2, ava, *vap;
1409*7c478bd9Sstevel@tonic-gate 	cred_t *cr = cs->cr;
1410*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp = cs->vp;
1411*7c478bd9Sstevel@tonic-gate 	vnode_t *vp = NULL;
1412*7c478bd9Sstevel@tonic-gate 	char *nm, *lnm;
1413*7c478bd9Sstevel@tonic-gate 	uint_t len, llen;
1414*7c478bd9Sstevel@tonic-gate 	int syncval = 0;
1415*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg sarg;
1416*7c478bd9Sstevel@tonic-gate 	struct nfs4_ntov_table ntov;
1417*7c478bd9Sstevel@tonic-gate 	struct statvfs64 sb;
1418*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
1419*7c478bd9Sstevel@tonic-gate 
1420*7c478bd9Sstevel@tonic-gate 	resp->attrset = 0;
1421*7c478bd9Sstevel@tonic-gate 
1422*7c478bd9Sstevel@tonic-gate 	if (dvp == NULL) {
1423*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
1424*7c478bd9Sstevel@tonic-gate 		return;
1425*7c478bd9Sstevel@tonic-gate 	}
1426*7c478bd9Sstevel@tonic-gate 
1427*7c478bd9Sstevel@tonic-gate 	/*
1428*7c478bd9Sstevel@tonic-gate 	 * If there is an unshared filesystem mounted on this vnode,
1429*7c478bd9Sstevel@tonic-gate 	 * do not allow to create an object in this directory.
1430*7c478bd9Sstevel@tonic-gate 	 */
1431*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(dvp)) {
1432*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
1433*7c478bd9Sstevel@tonic-gate 		return;
1434*7c478bd9Sstevel@tonic-gate 	}
1435*7c478bd9Sstevel@tonic-gate 
1436*7c478bd9Sstevel@tonic-gate 	/* Verify that type is correct */
1437*7c478bd9Sstevel@tonic-gate 	switch (args->type) {
1438*7c478bd9Sstevel@tonic-gate 	case NF4LNK:
1439*7c478bd9Sstevel@tonic-gate 	case NF4BLK:
1440*7c478bd9Sstevel@tonic-gate 	case NF4CHR:
1441*7c478bd9Sstevel@tonic-gate 	case NF4SOCK:
1442*7c478bd9Sstevel@tonic-gate 	case NF4FIFO:
1443*7c478bd9Sstevel@tonic-gate 	case NF4DIR:
1444*7c478bd9Sstevel@tonic-gate 		break;
1445*7c478bd9Sstevel@tonic-gate 	default:
1446*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADTYPE;
1447*7c478bd9Sstevel@tonic-gate 		return;
1448*7c478bd9Sstevel@tonic-gate 	};
1449*7c478bd9Sstevel@tonic-gate 
1450*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
1451*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
1452*7c478bd9Sstevel@tonic-gate 		return;
1453*7c478bd9Sstevel@tonic-gate 	}
1454*7c478bd9Sstevel@tonic-gate 	if (dvp->v_type != VDIR) {
1455*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
1456*7c478bd9Sstevel@tonic-gate 		return;
1457*7c478bd9Sstevel@tonic-gate 	}
1458*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(&args->objname)) {
1459*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
1460*7c478bd9Sstevel@tonic-gate 		return;
1461*7c478bd9Sstevel@tonic-gate 	}
1462*7c478bd9Sstevel@tonic-gate 
1463*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
1464*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
1465*7c478bd9Sstevel@tonic-gate 		return;
1466*7c478bd9Sstevel@tonic-gate 	}
1467*7c478bd9Sstevel@tonic-gate 
1468*7c478bd9Sstevel@tonic-gate 	/*
1469*7c478bd9Sstevel@tonic-gate 	 * Name of newly created object
1470*7c478bd9Sstevel@tonic-gate 	 */
1471*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_fn(&args->objname, &len, NULL);
1472*7c478bd9Sstevel@tonic-gate 	if (nm == NULL) {
1473*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
1474*7c478bd9Sstevel@tonic-gate 		return;
1475*7c478bd9Sstevel@tonic-gate 	}
1476*7c478bd9Sstevel@tonic-gate 
1477*7c478bd9Sstevel@tonic-gate 	if (len > MAXNAMELEN) {
1478*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
1479*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
1480*7c478bd9Sstevel@tonic-gate 		return;
1481*7c478bd9Sstevel@tonic-gate 	}
1482*7c478bd9Sstevel@tonic-gate 
1483*7c478bd9Sstevel@tonic-gate 	resp->attrset = 0;
1484*7c478bd9Sstevel@tonic-gate 
1485*7c478bd9Sstevel@tonic-gate 	sarg.sbp = &sb;
1486*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_init(&ntov);
1487*7c478bd9Sstevel@tonic-gate 
1488*7c478bd9Sstevel@tonic-gate 	status = do_rfs4_set_attrs(&resp->attrset,
1489*7c478bd9Sstevel@tonic-gate 					&args->createattrs, cs, &sarg,
1490*7c478bd9Sstevel@tonic-gate 					&ntov, NFS4ATTR_SETIT);
1491*7c478bd9Sstevel@tonic-gate 
1492*7c478bd9Sstevel@tonic-gate 	if (sarg.vap->va_mask == 0 && status == NFS4_OK)
1493*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_INVAL;
1494*7c478bd9Sstevel@tonic-gate 
1495*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
1496*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = status;
1497*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
1498*7c478bd9Sstevel@tonic-gate 		nfs4_ntov_table_free(&ntov, &sarg);
1499*7c478bd9Sstevel@tonic-gate 		resp->attrset = 0;
1500*7c478bd9Sstevel@tonic-gate 		return;
1501*7c478bd9Sstevel@tonic-gate 	}
1502*7c478bd9Sstevel@tonic-gate 
1503*7c478bd9Sstevel@tonic-gate 	/* Get "before" change value */
1504*7c478bd9Sstevel@tonic-gate 	bva.va_mask = AT_CTIME|AT_SEQ;
1505*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(dvp, &bva, 0, cr);
1506*7c478bd9Sstevel@tonic-gate 	if (error) {
1507*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1508*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
1509*7c478bd9Sstevel@tonic-gate 		nfs4_ntov_table_free(&ntov, &sarg);
1510*7c478bd9Sstevel@tonic-gate 		resp->attrset = 0;
1511*7c478bd9Sstevel@tonic-gate 		return;
1512*7c478bd9Sstevel@tonic-gate 	}
1513*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->cinfo.before, bva.va_ctime)
1514*7c478bd9Sstevel@tonic-gate 
1515*7c478bd9Sstevel@tonic-gate 	vap = sarg.vap;
1516*7c478bd9Sstevel@tonic-gate 
1517*7c478bd9Sstevel@tonic-gate 	/*
1518*7c478bd9Sstevel@tonic-gate 	 * Set default initial values for attributes when not specified
1519*7c478bd9Sstevel@tonic-gate 	 * in createattrs.
1520*7c478bd9Sstevel@tonic-gate 	 */
1521*7c478bd9Sstevel@tonic-gate 	if ((vap->va_mask & AT_UID) == 0) {
1522*7c478bd9Sstevel@tonic-gate 		vap->va_uid = crgetuid(cr);
1523*7c478bd9Sstevel@tonic-gate 		vap->va_mask |= AT_UID;
1524*7c478bd9Sstevel@tonic-gate 	}
1525*7c478bd9Sstevel@tonic-gate 	if ((vap->va_mask & AT_GID) == 0) {
1526*7c478bd9Sstevel@tonic-gate 		vap->va_gid = crgetgid(cr);
1527*7c478bd9Sstevel@tonic-gate 		vap->va_mask |= AT_GID;
1528*7c478bd9Sstevel@tonic-gate 	}
1529*7c478bd9Sstevel@tonic-gate 
1530*7c478bd9Sstevel@tonic-gate 	vap->va_mask |= AT_TYPE;
1531*7c478bd9Sstevel@tonic-gate 	switch (args->type) {
1532*7c478bd9Sstevel@tonic-gate 	case NF4DIR:
1533*7c478bd9Sstevel@tonic-gate 		vap->va_type = VDIR;
1534*7c478bd9Sstevel@tonic-gate 		if ((vap->va_mask & AT_MODE) == 0) {
1535*7c478bd9Sstevel@tonic-gate 			vap->va_mode = 0700;	/* default: owner rwx only */
1536*7c478bd9Sstevel@tonic-gate 			vap->va_mask |= AT_MODE;
1537*7c478bd9Sstevel@tonic-gate 		}
1538*7c478bd9Sstevel@tonic-gate 		error = VOP_MKDIR(dvp, nm, vap, &vp, cr);
1539*7c478bd9Sstevel@tonic-gate 		if (error)
1540*7c478bd9Sstevel@tonic-gate 			break;
1541*7c478bd9Sstevel@tonic-gate 
1542*7c478bd9Sstevel@tonic-gate 		/*
1543*7c478bd9Sstevel@tonic-gate 		 * Get the initial "after" sequence number, if it fails,
1544*7c478bd9Sstevel@tonic-gate 		 * set to zero
1545*7c478bd9Sstevel@tonic-gate 		 */
1546*7c478bd9Sstevel@tonic-gate 		iva.va_mask = AT_SEQ;
1547*7c478bd9Sstevel@tonic-gate 		if (VOP_GETATTR(dvp, &iva, 0, cs->cr))
1548*7c478bd9Sstevel@tonic-gate 			iva.va_seq = 0;
1549*7c478bd9Sstevel@tonic-gate 		break;
1550*7c478bd9Sstevel@tonic-gate 	case NF4LNK:
1551*7c478bd9Sstevel@tonic-gate 		vap->va_type = VLNK;
1552*7c478bd9Sstevel@tonic-gate 		if ((vap->va_mask & AT_MODE) == 0) {
1553*7c478bd9Sstevel@tonic-gate 			vap->va_mode = 0700;	/* default: owner rwx only */
1554*7c478bd9Sstevel@tonic-gate 			vap->va_mask |= AT_MODE;
1555*7c478bd9Sstevel@tonic-gate 		}
1556*7c478bd9Sstevel@tonic-gate 
1557*7c478bd9Sstevel@tonic-gate 		/*
1558*7c478bd9Sstevel@tonic-gate 		 * symlink names must be treated as data
1559*7c478bd9Sstevel@tonic-gate 		 */
1560*7c478bd9Sstevel@tonic-gate 		lnm = utf8_to_str(&args->ftype4_u.linkdata, &llen, NULL);
1561*7c478bd9Sstevel@tonic-gate 
1562*7c478bd9Sstevel@tonic-gate 		if (lnm == NULL) {
1563*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_INVAL;
1564*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, len);
1565*7c478bd9Sstevel@tonic-gate 			nfs4_ntov_table_free(&ntov, &sarg);
1566*7c478bd9Sstevel@tonic-gate 			resp->attrset = 0;
1567*7c478bd9Sstevel@tonic-gate 			return;
1568*7c478bd9Sstevel@tonic-gate 		}
1569*7c478bd9Sstevel@tonic-gate 
1570*7c478bd9Sstevel@tonic-gate 		if (llen > MAXPATHLEN) {
1571*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
1572*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, len);
1573*7c478bd9Sstevel@tonic-gate 			kmem_free(lnm, llen);
1574*7c478bd9Sstevel@tonic-gate 			nfs4_ntov_table_free(&ntov, &sarg);
1575*7c478bd9Sstevel@tonic-gate 			resp->attrset = 0;
1576*7c478bd9Sstevel@tonic-gate 			return;
1577*7c478bd9Sstevel@tonic-gate 		}
1578*7c478bd9Sstevel@tonic-gate 
1579*7c478bd9Sstevel@tonic-gate 		error = VOP_SYMLINK(dvp, nm, vap, lnm, cr);
1580*7c478bd9Sstevel@tonic-gate 		if (lnm != NULL)
1581*7c478bd9Sstevel@tonic-gate 			kmem_free(lnm, llen);
1582*7c478bd9Sstevel@tonic-gate 		if (error)
1583*7c478bd9Sstevel@tonic-gate 			break;
1584*7c478bd9Sstevel@tonic-gate 
1585*7c478bd9Sstevel@tonic-gate 		/*
1586*7c478bd9Sstevel@tonic-gate 		 * Get the initial "after" sequence number, if it fails,
1587*7c478bd9Sstevel@tonic-gate 		 * set to zero
1588*7c478bd9Sstevel@tonic-gate 		 */
1589*7c478bd9Sstevel@tonic-gate 		iva.va_mask = AT_SEQ;
1590*7c478bd9Sstevel@tonic-gate 		if (VOP_GETATTR(dvp, &iva, 0, cs->cr))
1591*7c478bd9Sstevel@tonic-gate 			iva.va_seq = 0;
1592*7c478bd9Sstevel@tonic-gate 
1593*7c478bd9Sstevel@tonic-gate 		error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cr);
1594*7c478bd9Sstevel@tonic-gate 		if (error)
1595*7c478bd9Sstevel@tonic-gate 			break;
1596*7c478bd9Sstevel@tonic-gate 
1597*7c478bd9Sstevel@tonic-gate 		VN_SETPATH(rootdir, dvp, vp, nm, strlen(nm));
1598*7c478bd9Sstevel@tonic-gate 
1599*7c478bd9Sstevel@tonic-gate 		/*
1600*7c478bd9Sstevel@tonic-gate 		 * va_seq is not safe over VOP calls, check it again
1601*7c478bd9Sstevel@tonic-gate 		 * if it has changed zero out iva to force atomic = FALSE.
1602*7c478bd9Sstevel@tonic-gate 		 */
1603*7c478bd9Sstevel@tonic-gate 		iva2.va_mask = AT_SEQ;
1604*7c478bd9Sstevel@tonic-gate 		if (VOP_GETATTR(dvp, &iva2, 0, cs->cr) ||
1605*7c478bd9Sstevel@tonic-gate 						iva2.va_seq != iva.va_seq)
1606*7c478bd9Sstevel@tonic-gate 			iva.va_seq = 0;
1607*7c478bd9Sstevel@tonic-gate 		break;
1608*7c478bd9Sstevel@tonic-gate 	default:
1609*7c478bd9Sstevel@tonic-gate 		/*
1610*7c478bd9Sstevel@tonic-gate 		 * probably a special file.
1611*7c478bd9Sstevel@tonic-gate 		 */
1612*7c478bd9Sstevel@tonic-gate 		if ((vap->va_mask & AT_MODE) == 0) {
1613*7c478bd9Sstevel@tonic-gate 			vap->va_mode = 0600;	/* default: owner rw only */
1614*7c478bd9Sstevel@tonic-gate 			vap->va_mask |= AT_MODE;
1615*7c478bd9Sstevel@tonic-gate 		}
1616*7c478bd9Sstevel@tonic-gate 		syncval = FNODSYNC;
1617*7c478bd9Sstevel@tonic-gate 		/*
1618*7c478bd9Sstevel@tonic-gate 		 * We know this will only generate one VOP call
1619*7c478bd9Sstevel@tonic-gate 		 */
1620*7c478bd9Sstevel@tonic-gate 		vp = do_rfs4_op_mknod(args, resp, req, cs, vap, nm);
1621*7c478bd9Sstevel@tonic-gate 
1622*7c478bd9Sstevel@tonic-gate 		if (vp == NULL) {
1623*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, len);
1624*7c478bd9Sstevel@tonic-gate 			nfs4_ntov_table_free(&ntov, &sarg);
1625*7c478bd9Sstevel@tonic-gate 			resp->attrset = 0;
1626*7c478bd9Sstevel@tonic-gate 			return;
1627*7c478bd9Sstevel@tonic-gate 		}
1628*7c478bd9Sstevel@tonic-gate 
1629*7c478bd9Sstevel@tonic-gate 		/*
1630*7c478bd9Sstevel@tonic-gate 		 * Get the initial "after" sequence number, if it fails,
1631*7c478bd9Sstevel@tonic-gate 		 * set to zero
1632*7c478bd9Sstevel@tonic-gate 		 */
1633*7c478bd9Sstevel@tonic-gate 		iva.va_mask = AT_SEQ;
1634*7c478bd9Sstevel@tonic-gate 		if (VOP_GETATTR(dvp, &iva, 0, cs->cr))
1635*7c478bd9Sstevel@tonic-gate 			iva.va_seq = 0;
1636*7c478bd9Sstevel@tonic-gate 
1637*7c478bd9Sstevel@tonic-gate 		break;
1638*7c478bd9Sstevel@tonic-gate 	}
1639*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, len);
1640*7c478bd9Sstevel@tonic-gate 
1641*7c478bd9Sstevel@tonic-gate 	if (error) {
1642*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1643*7c478bd9Sstevel@tonic-gate 	}
1644*7c478bd9Sstevel@tonic-gate 
1645*7c478bd9Sstevel@tonic-gate 	/*
1646*7c478bd9Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
1647*7c478bd9Sstevel@tonic-gate 	 */
1648*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(dvp, 0, cr);
1649*7c478bd9Sstevel@tonic-gate 
1650*7c478bd9Sstevel@tonic-gate 	if (resp->status != NFS4_OK) {
1651*7c478bd9Sstevel@tonic-gate 		if (vp != NULL)
1652*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
1653*7c478bd9Sstevel@tonic-gate 		nfs4_ntov_table_free(&ntov, &sarg);
1654*7c478bd9Sstevel@tonic-gate 		resp->attrset = 0;
1655*7c478bd9Sstevel@tonic-gate 		return;
1656*7c478bd9Sstevel@tonic-gate 	}
1657*7c478bd9Sstevel@tonic-gate 
1658*7c478bd9Sstevel@tonic-gate 	/*
1659*7c478bd9Sstevel@tonic-gate 	 * Finish setup of cinfo response, "before" value already set.
1660*7c478bd9Sstevel@tonic-gate 	 * Get "after" change value, if it fails, simply return the
1661*7c478bd9Sstevel@tonic-gate 	 * before value.
1662*7c478bd9Sstevel@tonic-gate 	 */
1663*7c478bd9Sstevel@tonic-gate 	ava.va_mask = AT_CTIME|AT_SEQ;
1664*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &ava, 0, cr)) {
1665*7c478bd9Sstevel@tonic-gate 		ava.va_ctime = bva.va_ctime;
1666*7c478bd9Sstevel@tonic-gate 		ava.va_seq = 0;
1667*7c478bd9Sstevel@tonic-gate 	}
1668*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->cinfo.after, ava.va_ctime);
1669*7c478bd9Sstevel@tonic-gate 
1670*7c478bd9Sstevel@tonic-gate 	/*
1671*7c478bd9Sstevel@tonic-gate 	 * True verification that object was created with correct
1672*7c478bd9Sstevel@tonic-gate 	 * attrs is impossible.  The attrs could have been changed
1673*7c478bd9Sstevel@tonic-gate 	 * immediately after object creation.  If attributes did
1674*7c478bd9Sstevel@tonic-gate 	 * not verify, the only recourse for the server is to
1675*7c478bd9Sstevel@tonic-gate 	 * destroy the object.  Maybe if some attrs (like gid)
1676*7c478bd9Sstevel@tonic-gate 	 * are set incorrectly, the object should be destroyed;
1677*7c478bd9Sstevel@tonic-gate 	 * however, seems bad as a default policy.  Do we really
1678*7c478bd9Sstevel@tonic-gate 	 * want to destroy an object over one of the times not
1679*7c478bd9Sstevel@tonic-gate 	 * verifying correctly?  For these reasons, the server
1680*7c478bd9Sstevel@tonic-gate 	 * currently sets bits in attrset for createattrs
1681*7c478bd9Sstevel@tonic-gate 	 * that were set; however, no verification is done.
1682*7c478bd9Sstevel@tonic-gate 	 *
1683*7c478bd9Sstevel@tonic-gate 	 * vmask_to_nmask accounts for vattr bits set on create
1684*7c478bd9Sstevel@tonic-gate 	 *	[do_rfs4_set_attrs() only sets resp bits for
1685*7c478bd9Sstevel@tonic-gate 	 *	 non-vattr/vfs bits.]
1686*7c478bd9Sstevel@tonic-gate 	 * Mask off any bits set by default so as not to return
1687*7c478bd9Sstevel@tonic-gate 	 * more attrset bits than were requested in createattrs
1688*7c478bd9Sstevel@tonic-gate 	 */
1689*7c478bd9Sstevel@tonic-gate 	nfs4_vmask_to_nmask(sarg.vap->va_mask, &resp->attrset);
1690*7c478bd9Sstevel@tonic-gate 	resp->attrset &= args->createattrs.attrmask;
1691*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_free(&ntov, &sarg);
1692*7c478bd9Sstevel@tonic-gate 
1693*7c478bd9Sstevel@tonic-gate 	error = makefh4(&cs->fh, vp, cs->exi);
1694*7c478bd9Sstevel@tonic-gate 	if (error) {
1695*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
1696*7c478bd9Sstevel@tonic-gate 	}
1697*7c478bd9Sstevel@tonic-gate 
1698*7c478bd9Sstevel@tonic-gate 	/*
1699*7c478bd9Sstevel@tonic-gate 	 * The cinfo.atomic = TRUE only if we got no errors, we have
1700*7c478bd9Sstevel@tonic-gate 	 * non-zero va_seq's, and it has incremented by exactly one
1701*7c478bd9Sstevel@tonic-gate 	 * during the creation and it didn't change during the VOP_LOOKUP
1702*7c478bd9Sstevel@tonic-gate 	 * or VOP_FSYNC.
1703*7c478bd9Sstevel@tonic-gate 	 */
1704*7c478bd9Sstevel@tonic-gate 	if (!error && bva.va_seq && iva.va_seq && ava.va_seq &&
1705*7c478bd9Sstevel@tonic-gate 			iva.va_seq == (bva.va_seq + 1) &&
1706*7c478bd9Sstevel@tonic-gate 			iva.va_seq == ava.va_seq)
1707*7c478bd9Sstevel@tonic-gate 		resp->cinfo.atomic = TRUE;
1708*7c478bd9Sstevel@tonic-gate 	else
1709*7c478bd9Sstevel@tonic-gate 		resp->cinfo.atomic = FALSE;
1710*7c478bd9Sstevel@tonic-gate 
1711*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(vp, syncval, cr);
1712*7c478bd9Sstevel@tonic-gate 
1713*7c478bd9Sstevel@tonic-gate 	if (resp->status != NFS4_OK) {
1714*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
1715*7c478bd9Sstevel@tonic-gate 		return;
1716*7c478bd9Sstevel@tonic-gate 	}
1717*7c478bd9Sstevel@tonic-gate 	if (cs->vp)
1718*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
1719*7c478bd9Sstevel@tonic-gate 
1720*7c478bd9Sstevel@tonic-gate 	cs->vp = vp;
1721*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
1722*7c478bd9Sstevel@tonic-gate }
1723*7c478bd9Sstevel@tonic-gate 
1724*7c478bd9Sstevel@tonic-gate 
1725*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1726*7c478bd9Sstevel@tonic-gate static void
1727*7c478bd9Sstevel@tonic-gate rfs4_op_delegreturn(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
1728*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
1729*7c478bd9Sstevel@tonic-gate {
1730*7c478bd9Sstevel@tonic-gate 	DELEGRETURN4args *args = &argop->nfs_argop4_u.opdelegreturn;
1731*7c478bd9Sstevel@tonic-gate 	DELEGRETURN4res *resp = &resop->nfs_resop4_u.opdelegreturn;
1732*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
1733*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
1734*7c478bd9Sstevel@tonic-gate 
1735*7c478bd9Sstevel@tonic-gate 	status = rfs4_get_deleg_state(&args->deleg_stateid, &dsp);
1736*7c478bd9Sstevel@tonic-gate 	resp->status = *cs->statusp = status;
1737*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK)
1738*7c478bd9Sstevel@tonic-gate 		return;
1739*7c478bd9Sstevel@tonic-gate 
1740*7c478bd9Sstevel@tonic-gate 	/* Ensure specified filehandle matches */
1741*7c478bd9Sstevel@tonic-gate 	if (cs->vp != dsp->finfo->vp) {
1742*7c478bd9Sstevel@tonic-gate 		resp->status = *cs->statusp = NFS4ERR_BAD_STATEID;
1743*7c478bd9Sstevel@tonic-gate 	} else
1744*7c478bd9Sstevel@tonic-gate 		rfs4_return_deleg(dsp, FALSE);
1745*7c478bd9Sstevel@tonic-gate 
1746*7c478bd9Sstevel@tonic-gate 	rfs4_update_lease(dsp->client);
1747*7c478bd9Sstevel@tonic-gate 
1748*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_rele(dsp);
1749*7c478bd9Sstevel@tonic-gate }
1750*7c478bd9Sstevel@tonic-gate 
1751*7c478bd9Sstevel@tonic-gate /*
1752*7c478bd9Sstevel@tonic-gate  * Check to see if a given "flavor" is an explicitly shared flavor.
1753*7c478bd9Sstevel@tonic-gate  * The assumption of this routine is the "flavor" is already a valid
1754*7c478bd9Sstevel@tonic-gate  * flavor in the secinfo list of "exi".
1755*7c478bd9Sstevel@tonic-gate  *
1756*7c478bd9Sstevel@tonic-gate  *	e.g.
1757*7c478bd9Sstevel@tonic-gate  *		# share -o sec=flavor1 /export
1758*7c478bd9Sstevel@tonic-gate  *		# share -o sec=flavor2 /export/home
1759*7c478bd9Sstevel@tonic-gate  *
1760*7c478bd9Sstevel@tonic-gate  *		flavor2 is not an explicitly shared flavor for /export,
1761*7c478bd9Sstevel@tonic-gate  *		however it is in the secinfo list for /export thru the
1762*7c478bd9Sstevel@tonic-gate  *		server namespace setup.
1763*7c478bd9Sstevel@tonic-gate  */
1764*7c478bd9Sstevel@tonic-gate int
1765*7c478bd9Sstevel@tonic-gate is_exported_sec(int flavor, struct exportinfo *exi)
1766*7c478bd9Sstevel@tonic-gate {
1767*7c478bd9Sstevel@tonic-gate 	int	i;
1768*7c478bd9Sstevel@tonic-gate 	struct secinfo *sp;
1769*7c478bd9Sstevel@tonic-gate 
1770*7c478bd9Sstevel@tonic-gate 	sp = exi->exi_export.ex_secinfo;
1771*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
1772*7c478bd9Sstevel@tonic-gate 		if (flavor == sp[i].s_secinfo.sc_nfsnum ||
1773*7c478bd9Sstevel@tonic-gate 		    sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
1774*7c478bd9Sstevel@tonic-gate 			return (SEC_REF_EXPORTED(&sp[i]));
1775*7c478bd9Sstevel@tonic-gate 		}
1776*7c478bd9Sstevel@tonic-gate 	}
1777*7c478bd9Sstevel@tonic-gate 
1778*7c478bd9Sstevel@tonic-gate 	/* Should not reach this point based on the assumption */
1779*7c478bd9Sstevel@tonic-gate 	return (0);
1780*7c478bd9Sstevel@tonic-gate }
1781*7c478bd9Sstevel@tonic-gate 
1782*7c478bd9Sstevel@tonic-gate /*
1783*7c478bd9Sstevel@tonic-gate  * Check if the security flavor used in the request matches what is
1784*7c478bd9Sstevel@tonic-gate  * required at the export point or at the root pseudo node (exi_root).
1785*7c478bd9Sstevel@tonic-gate  *
1786*7c478bd9Sstevel@tonic-gate  * returns 1 if there's a match or if exported with AUTH_NONE; 0 otherwise.
1787*7c478bd9Sstevel@tonic-gate  *
1788*7c478bd9Sstevel@tonic-gate  */
1789*7c478bd9Sstevel@tonic-gate static int
1790*7c478bd9Sstevel@tonic-gate secinfo_match_or_authnone(struct compound_state *cs)
1791*7c478bd9Sstevel@tonic-gate {
1792*7c478bd9Sstevel@tonic-gate 	int	i;
1793*7c478bd9Sstevel@tonic-gate 	struct secinfo *sp;
1794*7c478bd9Sstevel@tonic-gate 
1795*7c478bd9Sstevel@tonic-gate 	/*
1796*7c478bd9Sstevel@tonic-gate 	 * Check cs->nfsflavor (from the request) against
1797*7c478bd9Sstevel@tonic-gate 	 * the current export data in cs->exi.
1798*7c478bd9Sstevel@tonic-gate 	 */
1799*7c478bd9Sstevel@tonic-gate 	sp = cs->exi->exi_export.ex_secinfo;
1800*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < cs->exi->exi_export.ex_seccnt; i++) {
1801*7c478bd9Sstevel@tonic-gate 		if (cs->nfsflavor == sp[i].s_secinfo.sc_nfsnum ||
1802*7c478bd9Sstevel@tonic-gate 		    sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
1803*7c478bd9Sstevel@tonic-gate 			return (1);
1804*7c478bd9Sstevel@tonic-gate 	}
1805*7c478bd9Sstevel@tonic-gate 
1806*7c478bd9Sstevel@tonic-gate 	return (0);
1807*7c478bd9Sstevel@tonic-gate }
1808*7c478bd9Sstevel@tonic-gate 
1809*7c478bd9Sstevel@tonic-gate /*
1810*7c478bd9Sstevel@tonic-gate  * Check the access authority for the client and return the correct error.
1811*7c478bd9Sstevel@tonic-gate  */
1812*7c478bd9Sstevel@tonic-gate nfsstat4
1813*7c478bd9Sstevel@tonic-gate call_checkauth4(struct compound_state *cs, struct svc_req *req)
1814*7c478bd9Sstevel@tonic-gate {
1815*7c478bd9Sstevel@tonic-gate 	int	authres;
1816*7c478bd9Sstevel@tonic-gate 
1817*7c478bd9Sstevel@tonic-gate 	/*
1818*7c478bd9Sstevel@tonic-gate 	 * First, check if the security flavor used in the request
1819*7c478bd9Sstevel@tonic-gate 	 * are among the flavors set in the server namespace.
1820*7c478bd9Sstevel@tonic-gate 	 */
1821*7c478bd9Sstevel@tonic-gate 	if (!secinfo_match_or_authnone(cs)) {
1822*7c478bd9Sstevel@tonic-gate 		*cs->statusp = NFS4ERR_WRONGSEC;
1823*7c478bd9Sstevel@tonic-gate 		return (*cs->statusp);
1824*7c478bd9Sstevel@tonic-gate 	}
1825*7c478bd9Sstevel@tonic-gate 
1826*7c478bd9Sstevel@tonic-gate 	authres = checkauth4(cs, req);
1827*7c478bd9Sstevel@tonic-gate 
1828*7c478bd9Sstevel@tonic-gate 	if (authres > 0) {
1829*7c478bd9Sstevel@tonic-gate 		*cs->statusp = NFS4_OK;
1830*7c478bd9Sstevel@tonic-gate 		if (! (cs->access & CS_ACCESS_LIMITED))
1831*7c478bd9Sstevel@tonic-gate 			cs->access = CS_ACCESS_OK;
1832*7c478bd9Sstevel@tonic-gate 	} else if (authres == 0) {
1833*7c478bd9Sstevel@tonic-gate 		*cs->statusp = NFS4ERR_ACCESS;
1834*7c478bd9Sstevel@tonic-gate 	} else if (authres == -2) {
1835*7c478bd9Sstevel@tonic-gate 		*cs->statusp = NFS4ERR_WRONGSEC;
1836*7c478bd9Sstevel@tonic-gate 	} else {
1837*7c478bd9Sstevel@tonic-gate 		*cs->statusp = NFS4ERR_DELAY;
1838*7c478bd9Sstevel@tonic-gate 	}
1839*7c478bd9Sstevel@tonic-gate 	return (*cs->statusp);
1840*7c478bd9Sstevel@tonic-gate }
1841*7c478bd9Sstevel@tonic-gate 
1842*7c478bd9Sstevel@tonic-gate /*
1843*7c478bd9Sstevel@tonic-gate  * bitmap4_to_attrmask is called by getattr and readdir.
1844*7c478bd9Sstevel@tonic-gate  * It sets up the vattr mask and determines whether vfsstat call is needed
1845*7c478bd9Sstevel@tonic-gate  * based on the input bitmap.
1846*7c478bd9Sstevel@tonic-gate  * Returns nfsv4 status.
1847*7c478bd9Sstevel@tonic-gate  */
1848*7c478bd9Sstevel@tonic-gate static nfsstat4
1849*7c478bd9Sstevel@tonic-gate bitmap4_to_attrmask(bitmap4 breq, struct nfs4_svgetit_arg *sargp)
1850*7c478bd9Sstevel@tonic-gate {
1851*7c478bd9Sstevel@tonic-gate 	int i;
1852*7c478bd9Sstevel@tonic-gate 	uint_t	va_mask;
1853*7c478bd9Sstevel@tonic-gate 	struct statvfs64 *sbp = sargp->sbp;
1854*7c478bd9Sstevel@tonic-gate 
1855*7c478bd9Sstevel@tonic-gate 	sargp->sbp = NULL;
1856*7c478bd9Sstevel@tonic-gate 	sargp->flag = 0;
1857*7c478bd9Sstevel@tonic-gate 	sargp->rdattr_error = NFS4_OK;
1858*7c478bd9Sstevel@tonic-gate 	sargp->mntdfid_set = FALSE;
1859*7c478bd9Sstevel@tonic-gate 	if (sargp->cs->vp)
1860*7c478bd9Sstevel@tonic-gate 		sargp->xattr = get_fh4_flag(&sargp->cs->fh,
1861*7c478bd9Sstevel@tonic-gate 					    FH4_ATTRDIR | FH4_NAMEDATTR);
1862*7c478bd9Sstevel@tonic-gate 	else
1863*7c478bd9Sstevel@tonic-gate 		sargp->xattr = 0;
1864*7c478bd9Sstevel@tonic-gate 
1865*7c478bd9Sstevel@tonic-gate 	/*
1866*7c478bd9Sstevel@tonic-gate 	 * Set rdattr_error_req to true if return error per
1867*7c478bd9Sstevel@tonic-gate 	 * failed entry rather than fail the readdir.
1868*7c478bd9Sstevel@tonic-gate 	 */
1869*7c478bd9Sstevel@tonic-gate 	if (breq & FATTR4_RDATTR_ERROR_MASK)
1870*7c478bd9Sstevel@tonic-gate 		sargp->rdattr_error_req = 1;
1871*7c478bd9Sstevel@tonic-gate 	else
1872*7c478bd9Sstevel@tonic-gate 		sargp->rdattr_error_req = 0;
1873*7c478bd9Sstevel@tonic-gate 
1874*7c478bd9Sstevel@tonic-gate 	/*
1875*7c478bd9Sstevel@tonic-gate 	 * generate the va_mask
1876*7c478bd9Sstevel@tonic-gate 	 * Handle the easy cases first
1877*7c478bd9Sstevel@tonic-gate 	 */
1878*7c478bd9Sstevel@tonic-gate 	switch (breq) {
1879*7c478bd9Sstevel@tonic-gate 	case NFS4_NTOV_ATTR_MASK:
1880*7c478bd9Sstevel@tonic-gate 		sargp->vap->va_mask = NFS4_NTOV_ATTR_AT_MASK;
1881*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
1882*7c478bd9Sstevel@tonic-gate 
1883*7c478bd9Sstevel@tonic-gate 	case NFS4_FS_ATTR_MASK:
1884*7c478bd9Sstevel@tonic-gate 		sargp->vap->va_mask = NFS4_FS_ATTR_AT_MASK;
1885*7c478bd9Sstevel@tonic-gate 		sargp->sbp = sbp;
1886*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
1887*7c478bd9Sstevel@tonic-gate 
1888*7c478bd9Sstevel@tonic-gate 	case NFS4_NTOV_ATTR_CACHE_MASK:
1889*7c478bd9Sstevel@tonic-gate 		sargp->vap->va_mask = NFS4_NTOV_ATTR_CACHE_AT_MASK;
1890*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
1891*7c478bd9Sstevel@tonic-gate 
1892*7c478bd9Sstevel@tonic-gate 	case FATTR4_LEASE_TIME_MASK:
1893*7c478bd9Sstevel@tonic-gate 		sargp->vap->va_mask = 0;
1894*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
1895*7c478bd9Sstevel@tonic-gate 
1896*7c478bd9Sstevel@tonic-gate 	default:
1897*7c478bd9Sstevel@tonic-gate 		va_mask = 0;
1898*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < nfs4_ntov_map_size; i++) {
1899*7c478bd9Sstevel@tonic-gate 			if ((breq & nfs4_ntov_map[i].fbit) &&
1900*7c478bd9Sstevel@tonic-gate 							nfs4_ntov_map[i].vbit)
1901*7c478bd9Sstevel@tonic-gate 				va_mask |= nfs4_ntov_map[i].vbit;
1902*7c478bd9Sstevel@tonic-gate 		}
1903*7c478bd9Sstevel@tonic-gate 
1904*7c478bd9Sstevel@tonic-gate 		/*
1905*7c478bd9Sstevel@tonic-gate 		 * Check is vfsstat is needed
1906*7c478bd9Sstevel@tonic-gate 		 */
1907*7c478bd9Sstevel@tonic-gate 		if (breq & NFS4_FS_ATTR_MASK)
1908*7c478bd9Sstevel@tonic-gate 			sargp->sbp = sbp;
1909*7c478bd9Sstevel@tonic-gate 
1910*7c478bd9Sstevel@tonic-gate 		sargp->vap->va_mask = va_mask;
1911*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
1912*7c478bd9Sstevel@tonic-gate 	}
1913*7c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
1914*7c478bd9Sstevel@tonic-gate }
1915*7c478bd9Sstevel@tonic-gate 
1916*7c478bd9Sstevel@tonic-gate /*
1917*7c478bd9Sstevel@tonic-gate  * bitmap4_get_sysattrs is called by getattr and readdir.
1918*7c478bd9Sstevel@tonic-gate  * It calls both VOP_GETATTR and VFS_STATVFS calls to get the attrs.
1919*7c478bd9Sstevel@tonic-gate  * Returns nfsv4 status.
1920*7c478bd9Sstevel@tonic-gate  */
1921*7c478bd9Sstevel@tonic-gate static nfsstat4
1922*7c478bd9Sstevel@tonic-gate bitmap4_get_sysattrs(struct nfs4_svgetit_arg *sargp)
1923*7c478bd9Sstevel@tonic-gate {
1924*7c478bd9Sstevel@tonic-gate 	int error;
1925*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs = sargp->cs;
1926*7c478bd9Sstevel@tonic-gate 	vnode_t *vp = cs->vp;
1927*7c478bd9Sstevel@tonic-gate 
1928*7c478bd9Sstevel@tonic-gate 	if (sargp->sbp != NULL) {
1929*7c478bd9Sstevel@tonic-gate 		if (error = VFS_STATVFS(vp->v_vfsp, sargp->sbp)) {
1930*7c478bd9Sstevel@tonic-gate 			sargp->sbp = NULL;	/* to identify error */
1931*7c478bd9Sstevel@tonic-gate 			return (puterrno4(error));
1932*7c478bd9Sstevel@tonic-gate 		}
1933*7c478bd9Sstevel@tonic-gate 	}
1934*7c478bd9Sstevel@tonic-gate 
1935*7c478bd9Sstevel@tonic-gate 	return (rfs4_vop_getattr(vp, sargp->vap, 0, cs->cr));
1936*7c478bd9Sstevel@tonic-gate }
1937*7c478bd9Sstevel@tonic-gate 
1938*7c478bd9Sstevel@tonic-gate static void
1939*7c478bd9Sstevel@tonic-gate nfs4_ntov_table_init(struct nfs4_ntov_table *ntovp)
1940*7c478bd9Sstevel@tonic-gate {
1941*7c478bd9Sstevel@tonic-gate 	ntovp->na = kmem_zalloc(sizeof (union nfs4_attr_u) * nfs4_ntov_map_size,
1942*7c478bd9Sstevel@tonic-gate 			KM_SLEEP);
1943*7c478bd9Sstevel@tonic-gate 	ntovp->attrcnt = 0;
1944*7c478bd9Sstevel@tonic-gate 	ntovp->vfsstat = FALSE;
1945*7c478bd9Sstevel@tonic-gate }
1946*7c478bd9Sstevel@tonic-gate 
1947*7c478bd9Sstevel@tonic-gate static void
1948*7c478bd9Sstevel@tonic-gate nfs4_ntov_table_free(struct nfs4_ntov_table *ntovp,
1949*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg *sargp)
1950*7c478bd9Sstevel@tonic-gate {
1951*7c478bd9Sstevel@tonic-gate 	int i;
1952*7c478bd9Sstevel@tonic-gate 	union nfs4_attr_u *na;
1953*7c478bd9Sstevel@tonic-gate 	uint8_t *amap;
1954*7c478bd9Sstevel@tonic-gate 
1955*7c478bd9Sstevel@tonic-gate 	/*
1956*7c478bd9Sstevel@tonic-gate 	 * XXX Should do the same checks for whether the bit is set
1957*7c478bd9Sstevel@tonic-gate 	 */
1958*7c478bd9Sstevel@tonic-gate 	for (i = 0, na = ntovp->na, amap = ntovp->amap;
1959*7c478bd9Sstevel@tonic-gate 		i < ntovp->attrcnt; i++, na++, amap++) {
1960*7c478bd9Sstevel@tonic-gate 		(void) (*nfs4_ntov_map[*amap].sv_getit)(
1961*7c478bd9Sstevel@tonic-gate 			NFS4ATTR_FREEIT, sargp, na);
1962*7c478bd9Sstevel@tonic-gate 	}
1963*7c478bd9Sstevel@tonic-gate 	if ((sargp->op == NFS4ATTR_SETIT) || (sargp->op == NFS4ATTR_VERIT)) {
1964*7c478bd9Sstevel@tonic-gate 		/*
1965*7c478bd9Sstevel@tonic-gate 		 * xdr_free for getattr will be done later
1966*7c478bd9Sstevel@tonic-gate 		 */
1967*7c478bd9Sstevel@tonic-gate 		for (i = 0, na = ntovp->na, amap = ntovp->amap;
1968*7c478bd9Sstevel@tonic-gate 			i < ntovp->attrcnt; i++, na++, amap++) {
1969*7c478bd9Sstevel@tonic-gate 			xdr_free(nfs4_ntov_map[*amap].xfunc, (caddr_t)na);
1970*7c478bd9Sstevel@tonic-gate 		}
1971*7c478bd9Sstevel@tonic-gate 	}
1972*7c478bd9Sstevel@tonic-gate 	kmem_free(ntovp->na, sizeof (union nfs4_attr_u) * nfs4_ntov_map_size);
1973*7c478bd9Sstevel@tonic-gate }
1974*7c478bd9Sstevel@tonic-gate 
1975*7c478bd9Sstevel@tonic-gate /*
1976*7c478bd9Sstevel@tonic-gate  * do_rfs4_op_getattr gets the system attrs and converts into fattr4.
1977*7c478bd9Sstevel@tonic-gate  */
1978*7c478bd9Sstevel@tonic-gate static nfsstat4
1979*7c478bd9Sstevel@tonic-gate do_rfs4_op_getattr(bitmap4 breq, fattr4 *fattrp,
1980*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg *sargp)
1981*7c478bd9Sstevel@tonic-gate {
1982*7c478bd9Sstevel@tonic-gate 	int error = 0;
1983*7c478bd9Sstevel@tonic-gate 	int i, k;
1984*7c478bd9Sstevel@tonic-gate 	struct nfs4_ntov_table ntov;
1985*7c478bd9Sstevel@tonic-gate 	XDR xdr;
1986*7c478bd9Sstevel@tonic-gate 	ulong_t xdr_size;
1987*7c478bd9Sstevel@tonic-gate 	char *xdr_attrs;
1988*7c478bd9Sstevel@tonic-gate 	nfsstat4 status = NFS4_OK;
1989*7c478bd9Sstevel@tonic-gate 	nfsstat4 prev_rdattr_error = sargp->rdattr_error;
1990*7c478bd9Sstevel@tonic-gate 	union nfs4_attr_u *na;
1991*7c478bd9Sstevel@tonic-gate 	uint8_t *amap;
1992*7c478bd9Sstevel@tonic-gate 
1993*7c478bd9Sstevel@tonic-gate 	sargp->op = NFS4ATTR_GETIT;
1994*7c478bd9Sstevel@tonic-gate 	sargp->flag = 0;
1995*7c478bd9Sstevel@tonic-gate 
1996*7c478bd9Sstevel@tonic-gate 	fattrp->attrmask = 0;
1997*7c478bd9Sstevel@tonic-gate 	/* if no bits requested, then return empty fattr4 */
1998*7c478bd9Sstevel@tonic-gate 	if (breq == 0) {
1999*7c478bd9Sstevel@tonic-gate 		fattrp->attrlist4_len = 0;
2000*7c478bd9Sstevel@tonic-gate 		fattrp->attrlist4 = NULL;
2001*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
2002*7c478bd9Sstevel@tonic-gate 	}
2003*7c478bd9Sstevel@tonic-gate 
2004*7c478bd9Sstevel@tonic-gate 	/*
2005*7c478bd9Sstevel@tonic-gate 	 * return NFS4ERR_INVAL when client requests write-only attrs
2006*7c478bd9Sstevel@tonic-gate 	 */
2007*7c478bd9Sstevel@tonic-gate 	if (breq & (FATTR4_TIME_ACCESS_SET_MASK | FATTR4_TIME_MODIFY_SET_MASK))
2008*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
2009*7c478bd9Sstevel@tonic-gate 
2010*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_init(&ntov);
2011*7c478bd9Sstevel@tonic-gate 	na = ntov.na;
2012*7c478bd9Sstevel@tonic-gate 	amap = ntov.amap;
2013*7c478bd9Sstevel@tonic-gate 
2014*7c478bd9Sstevel@tonic-gate 	/*
2015*7c478bd9Sstevel@tonic-gate 	 * Now loop to get or verify the attrs
2016*7c478bd9Sstevel@tonic-gate 	 */
2017*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nfs4_ntov_map_size; i++) {
2018*7c478bd9Sstevel@tonic-gate 		if (breq & nfs4_ntov_map[i].fbit) {
2019*7c478bd9Sstevel@tonic-gate 			if ((*nfs4_ntov_map[i].sv_getit)(
2020*7c478bd9Sstevel@tonic-gate 				    NFS4ATTR_SUPPORTED, sargp, NULL) == 0) {
2021*7c478bd9Sstevel@tonic-gate 
2022*7c478bd9Sstevel@tonic-gate 				error = (*nfs4_ntov_map[i].sv_getit)(
2023*7c478bd9Sstevel@tonic-gate 						NFS4ATTR_GETIT, sargp, na);
2024*7c478bd9Sstevel@tonic-gate 
2025*7c478bd9Sstevel@tonic-gate 				/*
2026*7c478bd9Sstevel@tonic-gate 				 * Possible error values:
2027*7c478bd9Sstevel@tonic-gate 				 * >0 if sv_getit failed to
2028*7c478bd9Sstevel@tonic-gate 				 * get the attr; 0 if succeeded;
2029*7c478bd9Sstevel@tonic-gate 				 * <0 if rdattr_error and the
2030*7c478bd9Sstevel@tonic-gate 				 * attribute cannot be returned.
2031*7c478bd9Sstevel@tonic-gate 				 */
2032*7c478bd9Sstevel@tonic-gate 				if (error && !(sargp->rdattr_error_req))
2033*7c478bd9Sstevel@tonic-gate 					goto done;
2034*7c478bd9Sstevel@tonic-gate 				/*
2035*7c478bd9Sstevel@tonic-gate 				 * If error then just for entry
2036*7c478bd9Sstevel@tonic-gate 				 */
2037*7c478bd9Sstevel@tonic-gate 				if (error == 0) {
2038*7c478bd9Sstevel@tonic-gate 					fattrp->attrmask |=
2039*7c478bd9Sstevel@tonic-gate 						nfs4_ntov_map[i].fbit;
2040*7c478bd9Sstevel@tonic-gate 					*amap++ =
2041*7c478bd9Sstevel@tonic-gate 						(uint8_t)nfs4_ntov_map[i].nval;
2042*7c478bd9Sstevel@tonic-gate 					na++;
2043*7c478bd9Sstevel@tonic-gate 					(ntov.attrcnt)++;
2044*7c478bd9Sstevel@tonic-gate 				} else if ((error > 0) &&
2045*7c478bd9Sstevel@tonic-gate 					(sargp->rdattr_error == NFS4_OK)) {
2046*7c478bd9Sstevel@tonic-gate 					sargp->rdattr_error = puterrno4(error);
2047*7c478bd9Sstevel@tonic-gate 				}
2048*7c478bd9Sstevel@tonic-gate 				error = 0;
2049*7c478bd9Sstevel@tonic-gate 			}
2050*7c478bd9Sstevel@tonic-gate 		}
2051*7c478bd9Sstevel@tonic-gate 	}
2052*7c478bd9Sstevel@tonic-gate 
2053*7c478bd9Sstevel@tonic-gate 	/*
2054*7c478bd9Sstevel@tonic-gate 	 * If rdattr_error was set after the return value for it was assigned,
2055*7c478bd9Sstevel@tonic-gate 	 * update it.
2056*7c478bd9Sstevel@tonic-gate 	 */
2057*7c478bd9Sstevel@tonic-gate 	if (prev_rdattr_error != sargp->rdattr_error) {
2058*7c478bd9Sstevel@tonic-gate 		na = ntov.na;
2059*7c478bd9Sstevel@tonic-gate 		amap = ntov.amap;
2060*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < ntov.attrcnt; i++, na++, amap++) {
2061*7c478bd9Sstevel@tonic-gate 			k = *amap;
2062*7c478bd9Sstevel@tonic-gate 			if (k < FATTR4_RDATTR_ERROR) {
2063*7c478bd9Sstevel@tonic-gate 				continue;
2064*7c478bd9Sstevel@tonic-gate 			}
2065*7c478bd9Sstevel@tonic-gate 			if ((k == FATTR4_RDATTR_ERROR) &&
2066*7c478bd9Sstevel@tonic-gate 			    ((*nfs4_ntov_map[k].sv_getit)(
2067*7c478bd9Sstevel@tonic-gate 				NFS4ATTR_SUPPORTED, sargp, NULL) == 0)) {
2068*7c478bd9Sstevel@tonic-gate 
2069*7c478bd9Sstevel@tonic-gate 				(void) (*nfs4_ntov_map[k].sv_getit)(
2070*7c478bd9Sstevel@tonic-gate 						NFS4ATTR_GETIT, sargp, na);
2071*7c478bd9Sstevel@tonic-gate 			}
2072*7c478bd9Sstevel@tonic-gate 			break;
2073*7c478bd9Sstevel@tonic-gate 		}
2074*7c478bd9Sstevel@tonic-gate 	}
2075*7c478bd9Sstevel@tonic-gate 
2076*7c478bd9Sstevel@tonic-gate 	xdr_size = 0;
2077*7c478bd9Sstevel@tonic-gate 	na = ntov.na;
2078*7c478bd9Sstevel@tonic-gate 	amap = ntov.amap;
2079*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < ntov.attrcnt; i++, na++, amap++) {
2080*7c478bd9Sstevel@tonic-gate 		xdr_size += xdr_sizeof(nfs4_ntov_map[*amap].xfunc, na);
2081*7c478bd9Sstevel@tonic-gate 	}
2082*7c478bd9Sstevel@tonic-gate 
2083*7c478bd9Sstevel@tonic-gate 	fattrp->attrlist4_len = xdr_size;
2084*7c478bd9Sstevel@tonic-gate 	if (xdr_size) {
2085*7c478bd9Sstevel@tonic-gate 		/* freed by rfs4_op_getattr_free() */
2086*7c478bd9Sstevel@tonic-gate 		fattrp->attrlist4 = xdr_attrs = kmem_zalloc(xdr_size, KM_SLEEP);
2087*7c478bd9Sstevel@tonic-gate 
2088*7c478bd9Sstevel@tonic-gate 		xdrmem_create(&xdr, xdr_attrs, xdr_size, XDR_ENCODE);
2089*7c478bd9Sstevel@tonic-gate 
2090*7c478bd9Sstevel@tonic-gate 		na = ntov.na;
2091*7c478bd9Sstevel@tonic-gate 		amap = ntov.amap;
2092*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < ntov.attrcnt; i++, na++, amap++) {
2093*7c478bd9Sstevel@tonic-gate 			if (!(*nfs4_ntov_map[*amap].xfunc)(&xdr, na)) {
2094*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "do_rfs4_op_getattr: xdr "
2095*7c478bd9Sstevel@tonic-gate 					"encode of attribute %d failed\n",
2096*7c478bd9Sstevel@tonic-gate 					*amap);
2097*7c478bd9Sstevel@tonic-gate 				status = NFS4ERR_SERVERFAULT;
2098*7c478bd9Sstevel@tonic-gate 				break;
2099*7c478bd9Sstevel@tonic-gate 			}
2100*7c478bd9Sstevel@tonic-gate 		}
2101*7c478bd9Sstevel@tonic-gate 		/* xdrmem_destroy(&xdrs); */	/* NO-OP */
2102*7c478bd9Sstevel@tonic-gate 	} else {
2103*7c478bd9Sstevel@tonic-gate 		fattrp->attrlist4 = NULL;
2104*7c478bd9Sstevel@tonic-gate 	}
2105*7c478bd9Sstevel@tonic-gate done:
2106*7c478bd9Sstevel@tonic-gate 
2107*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_free(&ntov, sargp);
2108*7c478bd9Sstevel@tonic-gate 
2109*7c478bd9Sstevel@tonic-gate 	if (error != 0)
2110*7c478bd9Sstevel@tonic-gate 		status = puterrno4(error);
2111*7c478bd9Sstevel@tonic-gate 
2112*7c478bd9Sstevel@tonic-gate 	return (status);
2113*7c478bd9Sstevel@tonic-gate }
2114*7c478bd9Sstevel@tonic-gate 
2115*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2116*7c478bd9Sstevel@tonic-gate static void
2117*7c478bd9Sstevel@tonic-gate rfs4_op_getattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
2118*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2119*7c478bd9Sstevel@tonic-gate {
2120*7c478bd9Sstevel@tonic-gate 	GETATTR4args *args = &argop->nfs_argop4_u.opgetattr;
2121*7c478bd9Sstevel@tonic-gate 	GETATTR4res *resp = &resop->nfs_resop4_u.opgetattr;
2122*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg sarg;
2123*7c478bd9Sstevel@tonic-gate 	struct statvfs64 sb;
2124*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
2125*7c478bd9Sstevel@tonic-gate 
2126*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
2127*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2128*7c478bd9Sstevel@tonic-gate 		return;
2129*7c478bd9Sstevel@tonic-gate 	}
2130*7c478bd9Sstevel@tonic-gate 
2131*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
2132*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
2133*7c478bd9Sstevel@tonic-gate 		return;
2134*7c478bd9Sstevel@tonic-gate 	}
2135*7c478bd9Sstevel@tonic-gate 
2136*7c478bd9Sstevel@tonic-gate 	sarg.sbp = &sb;
2137*7c478bd9Sstevel@tonic-gate 	sarg.cs = cs;
2138*7c478bd9Sstevel@tonic-gate 
2139*7c478bd9Sstevel@tonic-gate 	status = bitmap4_to_attrmask(args->attr_request, &sarg);
2140*7c478bd9Sstevel@tonic-gate 	if (status == NFS4_OK) {
2141*7c478bd9Sstevel@tonic-gate 		status = bitmap4_get_sysattrs(&sarg);
2142*7c478bd9Sstevel@tonic-gate 		if (status == NFS4_OK)
2143*7c478bd9Sstevel@tonic-gate 			status = do_rfs4_op_getattr(args->attr_request,
2144*7c478bd9Sstevel@tonic-gate 				&resp->obj_attributes, &sarg);
2145*7c478bd9Sstevel@tonic-gate 	}
2146*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = status;
2147*7c478bd9Sstevel@tonic-gate }
2148*7c478bd9Sstevel@tonic-gate 
2149*7c478bd9Sstevel@tonic-gate static void
2150*7c478bd9Sstevel@tonic-gate rfs4_op_getattr_free(nfs_resop4 *resop)
2151*7c478bd9Sstevel@tonic-gate {
2152*7c478bd9Sstevel@tonic-gate 	GETATTR4res *resp = &resop->nfs_resop4_u.opgetattr;
2153*7c478bd9Sstevel@tonic-gate 
2154*7c478bd9Sstevel@tonic-gate 	nfs4_fattr4_free(&resp->obj_attributes);
2155*7c478bd9Sstevel@tonic-gate }
2156*7c478bd9Sstevel@tonic-gate 
2157*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2158*7c478bd9Sstevel@tonic-gate static void
2159*7c478bd9Sstevel@tonic-gate rfs4_op_getfh(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
2160*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2161*7c478bd9Sstevel@tonic-gate {
2162*7c478bd9Sstevel@tonic-gate 	GETFH4res *resp = &resop->nfs_resop4_u.opgetfh;
2163*7c478bd9Sstevel@tonic-gate 
2164*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
2165*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2166*7c478bd9Sstevel@tonic-gate 		return;
2167*7c478bd9Sstevel@tonic-gate 	}
2168*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
2169*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
2170*7c478bd9Sstevel@tonic-gate 		return;
2171*7c478bd9Sstevel@tonic-gate 	}
2172*7c478bd9Sstevel@tonic-gate 
2173*7c478bd9Sstevel@tonic-gate 	resp->object.nfs_fh4_val =
2174*7c478bd9Sstevel@tonic-gate 		kmem_alloc(cs->fh.nfs_fh4_len, KM_SLEEP);
2175*7c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(&cs->fh, &resp->object);
2176*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
2177*7c478bd9Sstevel@tonic-gate }
2178*7c478bd9Sstevel@tonic-gate 
2179*7c478bd9Sstevel@tonic-gate static void
2180*7c478bd9Sstevel@tonic-gate rfs4_op_getfh_free(nfs_resop4 *resop)
2181*7c478bd9Sstevel@tonic-gate {
2182*7c478bd9Sstevel@tonic-gate 	GETFH4res *resp = &resop->nfs_resop4_u.opgetfh;
2183*7c478bd9Sstevel@tonic-gate 
2184*7c478bd9Sstevel@tonic-gate 	if (resp->status == NFS4_OK &&
2185*7c478bd9Sstevel@tonic-gate 	    resp->object.nfs_fh4_val != NULL) {
2186*7c478bd9Sstevel@tonic-gate 		kmem_free(resp->object.nfs_fh4_val, resp->object.nfs_fh4_len);
2187*7c478bd9Sstevel@tonic-gate 		resp->object.nfs_fh4_val = NULL;
2188*7c478bd9Sstevel@tonic-gate 		resp->object.nfs_fh4_len = 0;
2189*7c478bd9Sstevel@tonic-gate 	}
2190*7c478bd9Sstevel@tonic-gate }
2191*7c478bd9Sstevel@tonic-gate 
2192*7c478bd9Sstevel@tonic-gate /*
2193*7c478bd9Sstevel@tonic-gate  * illegal: args: void
2194*7c478bd9Sstevel@tonic-gate  *	    res : status (NFS4ERR_OP_ILLEGAL)
2195*7c478bd9Sstevel@tonic-gate  */
2196*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2197*7c478bd9Sstevel@tonic-gate static void
2198*7c478bd9Sstevel@tonic-gate rfs4_op_illegal(nfs_argop4 *argop, nfs_resop4 *resop,
2199*7c478bd9Sstevel@tonic-gate 	struct svc_req *req, struct compound_state *cs)
2200*7c478bd9Sstevel@tonic-gate {
2201*7c478bd9Sstevel@tonic-gate 	ILLEGAL4res *resp = &resop->nfs_resop4_u.opillegal;
2202*7c478bd9Sstevel@tonic-gate 
2203*7c478bd9Sstevel@tonic-gate 	resop->resop = OP_ILLEGAL;
2204*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4ERR_OP_ILLEGAL;
2205*7c478bd9Sstevel@tonic-gate }
2206*7c478bd9Sstevel@tonic-gate 
2207*7c478bd9Sstevel@tonic-gate /*
2208*7c478bd9Sstevel@tonic-gate  * link: args: SAVED_FH: file, CURRENT_FH: target directory
2209*7c478bd9Sstevel@tonic-gate  *	 res: status. If success - CURRENT_FH unchanged, return change_info
2210*7c478bd9Sstevel@tonic-gate  */
2211*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2212*7c478bd9Sstevel@tonic-gate static void
2213*7c478bd9Sstevel@tonic-gate rfs4_op_link(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
2214*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2215*7c478bd9Sstevel@tonic-gate {
2216*7c478bd9Sstevel@tonic-gate 	LINK4args *args = &argop->nfs_argop4_u.oplink;
2217*7c478bd9Sstevel@tonic-gate 	LINK4res *resp = &resop->nfs_resop4_u.oplink;
2218*7c478bd9Sstevel@tonic-gate 	int error;
2219*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
2220*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp;
2221*7c478bd9Sstevel@tonic-gate 	struct vattr bdva, idva, adva;
2222*7c478bd9Sstevel@tonic-gate 	char *nm;
2223*7c478bd9Sstevel@tonic-gate 	uint_t  len;
2224*7c478bd9Sstevel@tonic-gate 
2225*7c478bd9Sstevel@tonic-gate 	/* SAVED_FH: source object */
2226*7c478bd9Sstevel@tonic-gate 	vp = cs->saved_vp;
2227*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
2228*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2229*7c478bd9Sstevel@tonic-gate 		return;
2230*7c478bd9Sstevel@tonic-gate 	}
2231*7c478bd9Sstevel@tonic-gate 
2232*7c478bd9Sstevel@tonic-gate 	/* CURRENT_FH: target directory */
2233*7c478bd9Sstevel@tonic-gate 	dvp = cs->vp;
2234*7c478bd9Sstevel@tonic-gate 	if (dvp == NULL) {
2235*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2236*7c478bd9Sstevel@tonic-gate 		return;
2237*7c478bd9Sstevel@tonic-gate 	}
2238*7c478bd9Sstevel@tonic-gate 
2239*7c478bd9Sstevel@tonic-gate 	/*
2240*7c478bd9Sstevel@tonic-gate 	 * If there is a non-shared filesystem mounted on this vnode,
2241*7c478bd9Sstevel@tonic-gate 	 * do not allow to link any file in this directory.
2242*7c478bd9Sstevel@tonic-gate 	 */
2243*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(dvp)) {
2244*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
2245*7c478bd9Sstevel@tonic-gate 		return;
2246*7c478bd9Sstevel@tonic-gate 	}
2247*7c478bd9Sstevel@tonic-gate 
2248*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
2249*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
2250*7c478bd9Sstevel@tonic-gate 		return;
2251*7c478bd9Sstevel@tonic-gate 	}
2252*7c478bd9Sstevel@tonic-gate 
2253*7c478bd9Sstevel@tonic-gate 	/* Check source object's type validity */
2254*7c478bd9Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
2255*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ISDIR;
2256*7c478bd9Sstevel@tonic-gate 		return;
2257*7c478bd9Sstevel@tonic-gate 	}
2258*7c478bd9Sstevel@tonic-gate 
2259*7c478bd9Sstevel@tonic-gate 	/* Check target directory's type */
2260*7c478bd9Sstevel@tonic-gate 	if (dvp->v_type != VDIR) {
2261*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
2262*7c478bd9Sstevel@tonic-gate 		return;
2263*7c478bd9Sstevel@tonic-gate 	}
2264*7c478bd9Sstevel@tonic-gate 
2265*7c478bd9Sstevel@tonic-gate 	if (cs->saved_exi != cs->exi) {
2266*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_XDEV;
2267*7c478bd9Sstevel@tonic-gate 		return;
2268*7c478bd9Sstevel@tonic-gate 	}
2269*7c478bd9Sstevel@tonic-gate 
2270*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(&args->newname)) {
2271*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
2272*7c478bd9Sstevel@tonic-gate 		return;
2273*7c478bd9Sstevel@tonic-gate 	}
2274*7c478bd9Sstevel@tonic-gate 
2275*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_fn(&args->newname, &len, NULL);
2276*7c478bd9Sstevel@tonic-gate 	if (nm == NULL) {
2277*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
2278*7c478bd9Sstevel@tonic-gate 		return;
2279*7c478bd9Sstevel@tonic-gate 	}
2280*7c478bd9Sstevel@tonic-gate 
2281*7c478bd9Sstevel@tonic-gate 	if (len > MAXNAMELEN) {
2282*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
2283*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
2284*7c478bd9Sstevel@tonic-gate 		return;
2285*7c478bd9Sstevel@tonic-gate 	}
2286*7c478bd9Sstevel@tonic-gate 
2287*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
2288*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
2289*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
2290*7c478bd9Sstevel@tonic-gate 		return;
2291*7c478bd9Sstevel@tonic-gate 	}
2292*7c478bd9Sstevel@tonic-gate 
2293*7c478bd9Sstevel@tonic-gate 	/* Get "before" change value */
2294*7c478bd9Sstevel@tonic-gate 	bdva.va_mask = AT_CTIME|AT_SEQ;
2295*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(dvp, &bdva, 0, cs->cr);
2296*7c478bd9Sstevel@tonic-gate 	if (error) {
2297*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
2298*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
2299*7c478bd9Sstevel@tonic-gate 		return;
2300*7c478bd9Sstevel@tonic-gate 	}
2301*7c478bd9Sstevel@tonic-gate 
2302*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->cinfo.before, bdva.va_ctime)
2303*7c478bd9Sstevel@tonic-gate 
2304*7c478bd9Sstevel@tonic-gate 	error = VOP_LINK(dvp, vp, nm, cs->cr);
2305*7c478bd9Sstevel@tonic-gate 
2306*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, len);
2307*7c478bd9Sstevel@tonic-gate 
2308*7c478bd9Sstevel@tonic-gate 	/*
2309*7c478bd9Sstevel@tonic-gate 	 * Get the initial "after" sequence number, if it fails, set to zero
2310*7c478bd9Sstevel@tonic-gate 	 */
2311*7c478bd9Sstevel@tonic-gate 	idva.va_mask = AT_SEQ;
2312*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &idva, 0, cs->cr))
2313*7c478bd9Sstevel@tonic-gate 		idva.va_seq = 0;
2314*7c478bd9Sstevel@tonic-gate 
2315*7c478bd9Sstevel@tonic-gate 	/*
2316*7c478bd9Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
2317*7c478bd9Sstevel@tonic-gate 	 */
2318*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(vp, FNODSYNC, cs->cr);
2319*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(dvp, 0, cs->cr);
2320*7c478bd9Sstevel@tonic-gate 
2321*7c478bd9Sstevel@tonic-gate 	if (error) {
2322*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
2323*7c478bd9Sstevel@tonic-gate 		return;
2324*7c478bd9Sstevel@tonic-gate 	}
2325*7c478bd9Sstevel@tonic-gate 
2326*7c478bd9Sstevel@tonic-gate 	/*
2327*7c478bd9Sstevel@tonic-gate 	 * Get "after" change value, if it fails, simply return the
2328*7c478bd9Sstevel@tonic-gate 	 * before value.
2329*7c478bd9Sstevel@tonic-gate 	 */
2330*7c478bd9Sstevel@tonic-gate 	adva.va_mask = AT_CTIME|AT_SEQ;
2331*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &adva, 0, cs->cr)) {
2332*7c478bd9Sstevel@tonic-gate 		adva.va_ctime = bdva.va_ctime;
2333*7c478bd9Sstevel@tonic-gate 		adva.va_seq = 0;
2334*7c478bd9Sstevel@tonic-gate 	}
2335*7c478bd9Sstevel@tonic-gate 
2336*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->cinfo.after, adva.va_ctime)
2337*7c478bd9Sstevel@tonic-gate 
2338*7c478bd9Sstevel@tonic-gate 	/*
2339*7c478bd9Sstevel@tonic-gate 	 * The cinfo.atomic = TRUE only if we have
2340*7c478bd9Sstevel@tonic-gate 	 * non-zero va_seq's, and it has incremented by exactly one
2341*7c478bd9Sstevel@tonic-gate 	 * during the VOP_LINK and it didn't change during the VOP_FSYNC.
2342*7c478bd9Sstevel@tonic-gate 	 */
2343*7c478bd9Sstevel@tonic-gate 	if (bdva.va_seq && idva.va_seq && adva.va_seq &&
2344*7c478bd9Sstevel@tonic-gate 			idva.va_seq == (bdva.va_seq + 1) &&
2345*7c478bd9Sstevel@tonic-gate 			idva.va_seq == adva.va_seq)
2346*7c478bd9Sstevel@tonic-gate 		resp->cinfo.atomic = TRUE;
2347*7c478bd9Sstevel@tonic-gate 	else
2348*7c478bd9Sstevel@tonic-gate 		resp->cinfo.atomic = FALSE;
2349*7c478bd9Sstevel@tonic-gate 
2350*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
2351*7c478bd9Sstevel@tonic-gate }
2352*7c478bd9Sstevel@tonic-gate 
2353*7c478bd9Sstevel@tonic-gate /*
2354*7c478bd9Sstevel@tonic-gate  * Used by rfs4_op_lookup and rfs4_op_lookupp to do the actual work.
2355*7c478bd9Sstevel@tonic-gate  */
2356*7c478bd9Sstevel@tonic-gate 
2357*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2358*7c478bd9Sstevel@tonic-gate static nfsstat4
2359*7c478bd9Sstevel@tonic-gate do_rfs4_op_lookup(char *nm, uint_t buflen, struct svc_req *req,
2360*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2361*7c478bd9Sstevel@tonic-gate {
2362*7c478bd9Sstevel@tonic-gate 	int error;
2363*7c478bd9Sstevel@tonic-gate 	int different_export = 0;
2364*7c478bd9Sstevel@tonic-gate 	vnode_t *vp, *tvp, *pre_tvp = NULL, *oldvp = NULL;
2365*7c478bd9Sstevel@tonic-gate 	struct exportinfo *exi = NULL, *pre_exi = NULL;
2366*7c478bd9Sstevel@tonic-gate 	nfsstat4 stat;
2367*7c478bd9Sstevel@tonic-gate 	fid_t fid;
2368*7c478bd9Sstevel@tonic-gate 	int attrdir, dotdot, walk;
2369*7c478bd9Sstevel@tonic-gate 	bool_t is_newvp = FALSE;
2370*7c478bd9Sstevel@tonic-gate 
2371*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_flag & V_XATTRDIR) {
2372*7c478bd9Sstevel@tonic-gate 		attrdir = 1;
2373*7c478bd9Sstevel@tonic-gate 		ASSERT(get_fh4_flag(&cs->fh, FH4_ATTRDIR));
2374*7c478bd9Sstevel@tonic-gate 	} else {
2375*7c478bd9Sstevel@tonic-gate 		attrdir = 0;
2376*7c478bd9Sstevel@tonic-gate 		ASSERT(! get_fh4_flag(&cs->fh, FH4_ATTRDIR));
2377*7c478bd9Sstevel@tonic-gate 	}
2378*7c478bd9Sstevel@tonic-gate 
2379*7c478bd9Sstevel@tonic-gate 	dotdot = (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0');
2380*7c478bd9Sstevel@tonic-gate 
2381*7c478bd9Sstevel@tonic-gate 	/*
2382*7c478bd9Sstevel@tonic-gate 	 * If dotdotting, then need to check whether it's
2383*7c478bd9Sstevel@tonic-gate 	 * above the root of a filesystem, or above an
2384*7c478bd9Sstevel@tonic-gate 	 * export point.
2385*7c478bd9Sstevel@tonic-gate 	 */
2386*7c478bd9Sstevel@tonic-gate 	if (dotdot) {
2387*7c478bd9Sstevel@tonic-gate 
2388*7c478bd9Sstevel@tonic-gate 		/*
2389*7c478bd9Sstevel@tonic-gate 		 * If dotdotting at the root of a filesystem, then
2390*7c478bd9Sstevel@tonic-gate 		 * need to traverse back to the mounted-on filesystem
2391*7c478bd9Sstevel@tonic-gate 		 * and do the dotdot lookup there.
2392*7c478bd9Sstevel@tonic-gate 		 */
2393*7c478bd9Sstevel@tonic-gate 		if (cs->vp->v_flag & VROOT) {
2394*7c478bd9Sstevel@tonic-gate 
2395*7c478bd9Sstevel@tonic-gate 			/*
2396*7c478bd9Sstevel@tonic-gate 			 * If at the system root, then can
2397*7c478bd9Sstevel@tonic-gate 			 * go up no further.
2398*7c478bd9Sstevel@tonic-gate 			 */
2399*7c478bd9Sstevel@tonic-gate 			if (VN_CMP(cs->vp, rootdir))
2400*7c478bd9Sstevel@tonic-gate 				return (puterrno4(ENOENT));
2401*7c478bd9Sstevel@tonic-gate 
2402*7c478bd9Sstevel@tonic-gate 			/*
2403*7c478bd9Sstevel@tonic-gate 			 * Traverse back to the mounted-on filesystem
2404*7c478bd9Sstevel@tonic-gate 			 */
2405*7c478bd9Sstevel@tonic-gate 			cs->vp = untraverse(cs->vp);
2406*7c478bd9Sstevel@tonic-gate 
2407*7c478bd9Sstevel@tonic-gate 			/*
2408*7c478bd9Sstevel@tonic-gate 			 * Set the different_export flag so we remember
2409*7c478bd9Sstevel@tonic-gate 			 * to pick up a new exportinfo entry for
2410*7c478bd9Sstevel@tonic-gate 			 * this new filesystem.
2411*7c478bd9Sstevel@tonic-gate 			 */
2412*7c478bd9Sstevel@tonic-gate 			different_export = 1;
2413*7c478bd9Sstevel@tonic-gate 		} else {
2414*7c478bd9Sstevel@tonic-gate 
2415*7c478bd9Sstevel@tonic-gate 			/*
2416*7c478bd9Sstevel@tonic-gate 			 * If dotdotting above an export point then set
2417*7c478bd9Sstevel@tonic-gate 			 * the different_export to get new export info.
2418*7c478bd9Sstevel@tonic-gate 			 */
2419*7c478bd9Sstevel@tonic-gate 			different_export = nfs_exported(cs->exi, cs->vp);
2420*7c478bd9Sstevel@tonic-gate 		}
2421*7c478bd9Sstevel@tonic-gate 	}
2422*7c478bd9Sstevel@tonic-gate 
2423*7c478bd9Sstevel@tonic-gate 	error = VOP_LOOKUP(cs->vp, nm, &vp, NULL, 0, NULL, cs->cr);
2424*7c478bd9Sstevel@tonic-gate 	if (error)
2425*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
2426*7c478bd9Sstevel@tonic-gate 
2427*7c478bd9Sstevel@tonic-gate 	VN_SETPATH(rootdir, cs->vp, vp, nm, strlen(nm));
2428*7c478bd9Sstevel@tonic-gate 
2429*7c478bd9Sstevel@tonic-gate 	/*
2430*7c478bd9Sstevel@tonic-gate 	 * If the vnode is in a pseudo filesystem, check whether it is visible.
2431*7c478bd9Sstevel@tonic-gate 	 *
2432*7c478bd9Sstevel@tonic-gate 	 * XXX if the vnode is a symlink and it is not visible in
2433*7c478bd9Sstevel@tonic-gate 	 * a pseudo filesystem, return ENOENT (not following symlink).
2434*7c478bd9Sstevel@tonic-gate 	 * V4 client can not mount such symlink. This is a regression
2435*7c478bd9Sstevel@tonic-gate 	 * from V2/V3.
2436*7c478bd9Sstevel@tonic-gate 	 *
2437*7c478bd9Sstevel@tonic-gate 	 * In the same exported filesystem, if the security flavor used
2438*7c478bd9Sstevel@tonic-gate 	 * is not an explicitly shared flavor, limit the view to the visible
2439*7c478bd9Sstevel@tonic-gate 	 * list entries only. This is not a WRONGSEC case because it's already
2440*7c478bd9Sstevel@tonic-gate 	 * checked via PUTROOTFH/PUTPUBFH or PUTFH.
2441*7c478bd9Sstevel@tonic-gate 	 */
2442*7c478bd9Sstevel@tonic-gate 	if (!different_export &&
2443*7c478bd9Sstevel@tonic-gate 	    (PSEUDO(cs->exi) || ! is_exported_sec(cs->nfsflavor, cs->exi) ||
2444*7c478bd9Sstevel@tonic-gate 	    cs->access & CS_ACCESS_LIMITED)) {
2445*7c478bd9Sstevel@tonic-gate 		if (! nfs_visible(cs->exi, vp, &different_export)) {
2446*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
2447*7c478bd9Sstevel@tonic-gate 			return (puterrno4(ENOENT));
2448*7c478bd9Sstevel@tonic-gate 		}
2449*7c478bd9Sstevel@tonic-gate 	}
2450*7c478bd9Sstevel@tonic-gate 
2451*7c478bd9Sstevel@tonic-gate 	/*
2452*7c478bd9Sstevel@tonic-gate 	 * If it's a mountpoint, then traverse it.
2453*7c478bd9Sstevel@tonic-gate 	 */
2454*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(vp)) {
2455*7c478bd9Sstevel@tonic-gate 		pre_exi = cs->exi;	/* save pre-traversed exportinfo */
2456*7c478bd9Sstevel@tonic-gate 		pre_tvp = vp;		/* save pre-traversed vnode	*/
2457*7c478bd9Sstevel@tonic-gate 
2458*7c478bd9Sstevel@tonic-gate 		/*
2459*7c478bd9Sstevel@tonic-gate 		 * hold pre_tvp to counteract rele by traverse.  We will
2460*7c478bd9Sstevel@tonic-gate 		 * need pre_tvp below if checkexport4 fails
2461*7c478bd9Sstevel@tonic-gate 		 */
2462*7c478bd9Sstevel@tonic-gate 		VN_HOLD(pre_tvp);
2463*7c478bd9Sstevel@tonic-gate 		tvp = vp;
2464*7c478bd9Sstevel@tonic-gate 		if ((error = traverse(&tvp)) != 0) {
2465*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
2466*7c478bd9Sstevel@tonic-gate 			VN_RELE(pre_tvp);
2467*7c478bd9Sstevel@tonic-gate 			return (puterrno4(error));
2468*7c478bd9Sstevel@tonic-gate 		}
2469*7c478bd9Sstevel@tonic-gate 		vp = tvp;
2470*7c478bd9Sstevel@tonic-gate 		different_export = 1;
2471*7c478bd9Sstevel@tonic-gate 	} else if (vp->v_vfsp != cs->vp->v_vfsp) {
2472*7c478bd9Sstevel@tonic-gate 		/*
2473*7c478bd9Sstevel@tonic-gate 		 * The vfsp comparison is to handle the case where
2474*7c478bd9Sstevel@tonic-gate 		 * a LOFS mount is shared.  lo_lookup traverses mount points,
2475*7c478bd9Sstevel@tonic-gate 		 * and NFS is unaware of local fs transistions because
2476*7c478bd9Sstevel@tonic-gate 		 * v_vfsmountedhere isn't set.  For this special LOFS case,
2477*7c478bd9Sstevel@tonic-gate 		 * the dir and the obj returned by lookup will have different
2478*7c478bd9Sstevel@tonic-gate 		 * vfs ptrs.
2479*7c478bd9Sstevel@tonic-gate 		 */
2480*7c478bd9Sstevel@tonic-gate 		different_export = 1;
2481*7c478bd9Sstevel@tonic-gate 	}
2482*7c478bd9Sstevel@tonic-gate 
2483*7c478bd9Sstevel@tonic-gate 	if (different_export) {
2484*7c478bd9Sstevel@tonic-gate 
2485*7c478bd9Sstevel@tonic-gate 		bzero(&fid, sizeof (fid));
2486*7c478bd9Sstevel@tonic-gate 		fid.fid_len = MAXFIDSZ;
2487*7c478bd9Sstevel@tonic-gate 		error = vop_fid_pseudo(vp, &fid);
2488*7c478bd9Sstevel@tonic-gate 		if (error) {
2489*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
2490*7c478bd9Sstevel@tonic-gate 			if (pre_tvp)
2491*7c478bd9Sstevel@tonic-gate 				VN_RELE(pre_tvp);
2492*7c478bd9Sstevel@tonic-gate 			return (puterrno4(error));
2493*7c478bd9Sstevel@tonic-gate 		}
2494*7c478bd9Sstevel@tonic-gate 
2495*7c478bd9Sstevel@tonic-gate 		if (dotdot)
2496*7c478bd9Sstevel@tonic-gate 			exi = nfs_vptoexi(NULL, vp, cs->cr, &walk, NULL, TRUE);
2497*7c478bd9Sstevel@tonic-gate 		else
2498*7c478bd9Sstevel@tonic-gate 			exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp);
2499*7c478bd9Sstevel@tonic-gate 
2500*7c478bd9Sstevel@tonic-gate 		if (exi == NULL) {
2501*7c478bd9Sstevel@tonic-gate 			if (pre_tvp) {
2502*7c478bd9Sstevel@tonic-gate 				/*
2503*7c478bd9Sstevel@tonic-gate 				 * If this vnode is a mounted-on vnode,
2504*7c478bd9Sstevel@tonic-gate 				 * but the mounted-on file system is not
2505*7c478bd9Sstevel@tonic-gate 				 * exported, send back the filehandle for
2506*7c478bd9Sstevel@tonic-gate 				 * the mounted-on vnode, not the root of
2507*7c478bd9Sstevel@tonic-gate 				 * the mounted-on file system.
2508*7c478bd9Sstevel@tonic-gate 				 */
2509*7c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
2510*7c478bd9Sstevel@tonic-gate 				vp = pre_tvp;
2511*7c478bd9Sstevel@tonic-gate 				exi = pre_exi;
2512*7c478bd9Sstevel@tonic-gate 			} else {
2513*7c478bd9Sstevel@tonic-gate 				VN_RELE(vp);
2514*7c478bd9Sstevel@tonic-gate 				return (puterrno4(EACCES));
2515*7c478bd9Sstevel@tonic-gate 			}
2516*7c478bd9Sstevel@tonic-gate 		} else if (pre_tvp) {
2517*7c478bd9Sstevel@tonic-gate 			/* we're done with pre_tvp now. release extra hold */
2518*7c478bd9Sstevel@tonic-gate 			VN_RELE(pre_tvp);
2519*7c478bd9Sstevel@tonic-gate 		}
2520*7c478bd9Sstevel@tonic-gate 
2521*7c478bd9Sstevel@tonic-gate 		cs->exi = exi;
2522*7c478bd9Sstevel@tonic-gate 
2523*7c478bd9Sstevel@tonic-gate 		/*
2524*7c478bd9Sstevel@tonic-gate 		 * Now we do a checkauth4. The reason is that
2525*7c478bd9Sstevel@tonic-gate 		 * this client/user may not have access to the new
2526*7c478bd9Sstevel@tonic-gate 		 * exported file system, and if he does,
2527*7c478bd9Sstevel@tonic-gate 		 * the client/user may be mapped to a different uid.
2528*7c478bd9Sstevel@tonic-gate 		 *
2529*7c478bd9Sstevel@tonic-gate 		 * We start with a new cr, because the checkauth4 done
2530*7c478bd9Sstevel@tonic-gate 		 * in the PUT*FH operation over wrote the cred's uid,
2531*7c478bd9Sstevel@tonic-gate 		 * gid, etc, and we want the real thing before calling
2532*7c478bd9Sstevel@tonic-gate 		 * checkauth4()
2533*7c478bd9Sstevel@tonic-gate 		 */
2534*7c478bd9Sstevel@tonic-gate 		crfree(cs->cr);
2535*7c478bd9Sstevel@tonic-gate 		cs->cr = crdup(cs->basecr);
2536*7c478bd9Sstevel@tonic-gate 
2537*7c478bd9Sstevel@tonic-gate 		if (cs->vp)
2538*7c478bd9Sstevel@tonic-gate 			oldvp = cs->vp;
2539*7c478bd9Sstevel@tonic-gate 		cs->vp = vp;
2540*7c478bd9Sstevel@tonic-gate 		is_newvp = TRUE;
2541*7c478bd9Sstevel@tonic-gate 
2542*7c478bd9Sstevel@tonic-gate 		stat = call_checkauth4(cs, req);
2543*7c478bd9Sstevel@tonic-gate 		if (stat != NFS4_OK) {
2544*7c478bd9Sstevel@tonic-gate 			VN_RELE(cs->vp);
2545*7c478bd9Sstevel@tonic-gate 			cs->vp = oldvp;
2546*7c478bd9Sstevel@tonic-gate 			return (stat);
2547*7c478bd9Sstevel@tonic-gate 		}
2548*7c478bd9Sstevel@tonic-gate 	}
2549*7c478bd9Sstevel@tonic-gate 
2550*7c478bd9Sstevel@tonic-gate 	error = makefh4(&cs->fh, vp, cs->exi);
2551*7c478bd9Sstevel@tonic-gate 
2552*7c478bd9Sstevel@tonic-gate 	if (error) {
2553*7c478bd9Sstevel@tonic-gate 		if (is_newvp) {
2554*7c478bd9Sstevel@tonic-gate 			VN_RELE(cs->vp);
2555*7c478bd9Sstevel@tonic-gate 			cs->vp = oldvp;
2556*7c478bd9Sstevel@tonic-gate 		} else
2557*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
2558*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
2559*7c478bd9Sstevel@tonic-gate 	}
2560*7c478bd9Sstevel@tonic-gate 
2561*7c478bd9Sstevel@tonic-gate 	if (!is_newvp) {
2562*7c478bd9Sstevel@tonic-gate 		if (cs->vp)
2563*7c478bd9Sstevel@tonic-gate 			VN_RELE(cs->vp);
2564*7c478bd9Sstevel@tonic-gate 		cs->vp = vp;
2565*7c478bd9Sstevel@tonic-gate 	} else if (oldvp)
2566*7c478bd9Sstevel@tonic-gate 		VN_RELE(oldvp);
2567*7c478bd9Sstevel@tonic-gate 
2568*7c478bd9Sstevel@tonic-gate 	/*
2569*7c478bd9Sstevel@tonic-gate 	 * if did lookup on attrdir and didn't lookup .., set named
2570*7c478bd9Sstevel@tonic-gate 	 * attr fh flag
2571*7c478bd9Sstevel@tonic-gate 	 */
2572*7c478bd9Sstevel@tonic-gate 	if (attrdir && ! dotdot)
2573*7c478bd9Sstevel@tonic-gate 		set_fh4_flag(&cs->fh, FH4_NAMEDATTR);
2574*7c478bd9Sstevel@tonic-gate 
2575*7c478bd9Sstevel@tonic-gate 	/* Assume false for now, open proc will set this */
2576*7c478bd9Sstevel@tonic-gate 	cs->mandlock = FALSE;
2577*7c478bd9Sstevel@tonic-gate 
2578*7c478bd9Sstevel@tonic-gate 	return (NFS4_OK);
2579*7c478bd9Sstevel@tonic-gate }
2580*7c478bd9Sstevel@tonic-gate 
2581*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2582*7c478bd9Sstevel@tonic-gate static void
2583*7c478bd9Sstevel@tonic-gate rfs4_op_lookup(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
2584*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2585*7c478bd9Sstevel@tonic-gate {
2586*7c478bd9Sstevel@tonic-gate 	LOOKUP4args *args = &argop->nfs_argop4_u.oplookup;
2587*7c478bd9Sstevel@tonic-gate 	LOOKUP4res *resp = &resop->nfs_resop4_u.oplookup;
2588*7c478bd9Sstevel@tonic-gate 	char *nm;
2589*7c478bd9Sstevel@tonic-gate 	uint_t len;
2590*7c478bd9Sstevel@tonic-gate 
2591*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
2592*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2593*7c478bd9Sstevel@tonic-gate 		return;
2594*7c478bd9Sstevel@tonic-gate 	}
2595*7c478bd9Sstevel@tonic-gate 
2596*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type == VLNK) {
2597*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_SYMLINK;
2598*7c478bd9Sstevel@tonic-gate 		return;
2599*7c478bd9Sstevel@tonic-gate 	}
2600*7c478bd9Sstevel@tonic-gate 
2601*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VDIR) {
2602*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
2603*7c478bd9Sstevel@tonic-gate 		return;
2604*7c478bd9Sstevel@tonic-gate 	}
2605*7c478bd9Sstevel@tonic-gate 
2606*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(&args->objname)) {
2607*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
2608*7c478bd9Sstevel@tonic-gate 		return;
2609*7c478bd9Sstevel@tonic-gate 	}
2610*7c478bd9Sstevel@tonic-gate 
2611*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_str(&args->objname, &len, NULL);
2612*7c478bd9Sstevel@tonic-gate 	if (nm == NULL) {
2613*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
2614*7c478bd9Sstevel@tonic-gate 		return;
2615*7c478bd9Sstevel@tonic-gate 	}
2616*7c478bd9Sstevel@tonic-gate 
2617*7c478bd9Sstevel@tonic-gate 	if (len > MAXNAMELEN) {
2618*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
2619*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
2620*7c478bd9Sstevel@tonic-gate 		return;
2621*7c478bd9Sstevel@tonic-gate 	}
2622*7c478bd9Sstevel@tonic-gate 
2623*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = do_rfs4_op_lookup(nm, len, req, cs);
2624*7c478bd9Sstevel@tonic-gate 
2625*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, len);
2626*7c478bd9Sstevel@tonic-gate }
2627*7c478bd9Sstevel@tonic-gate 
2628*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2629*7c478bd9Sstevel@tonic-gate static void
2630*7c478bd9Sstevel@tonic-gate rfs4_op_lookupp(nfs_argop4 *args, nfs_resop4 *resop, struct svc_req *req,
2631*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2632*7c478bd9Sstevel@tonic-gate {
2633*7c478bd9Sstevel@tonic-gate 	LOOKUPP4res *resp = &resop->nfs_resop4_u.oplookupp;
2634*7c478bd9Sstevel@tonic-gate 
2635*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
2636*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2637*7c478bd9Sstevel@tonic-gate 		return;
2638*7c478bd9Sstevel@tonic-gate 	}
2639*7c478bd9Sstevel@tonic-gate 
2640*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VDIR) {
2641*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
2642*7c478bd9Sstevel@tonic-gate 		return;
2643*7c478bd9Sstevel@tonic-gate 	}
2644*7c478bd9Sstevel@tonic-gate 
2645*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = do_rfs4_op_lookup("..", 3, req, cs);
2646*7c478bd9Sstevel@tonic-gate 
2647*7c478bd9Sstevel@tonic-gate 	/*
2648*7c478bd9Sstevel@tonic-gate 	 * From NFSV4 Specification, LOOKUPP should not check for
2649*7c478bd9Sstevel@tonic-gate 	 * NFS4ERR_WRONGSEC. Retrun NFS4_OK instead.
2650*7c478bd9Sstevel@tonic-gate 	 */
2651*7c478bd9Sstevel@tonic-gate 	if (resp->status == NFS4ERR_WRONGSEC) {
2652*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
2653*7c478bd9Sstevel@tonic-gate 	}
2654*7c478bd9Sstevel@tonic-gate }
2655*7c478bd9Sstevel@tonic-gate 
2656*7c478bd9Sstevel@tonic-gate 
2657*7c478bd9Sstevel@tonic-gate /*ARGSUSED2*/
2658*7c478bd9Sstevel@tonic-gate static void
2659*7c478bd9Sstevel@tonic-gate rfs4_op_openattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
2660*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2661*7c478bd9Sstevel@tonic-gate {
2662*7c478bd9Sstevel@tonic-gate 	OPENATTR4args	*args = &argop->nfs_argop4_u.opopenattr;
2663*7c478bd9Sstevel@tonic-gate 	OPENATTR4res	*resp = &resop->nfs_resop4_u.opopenattr;
2664*7c478bd9Sstevel@tonic-gate 	vnode_t		*avp = NULL;
2665*7c478bd9Sstevel@tonic-gate 	int		lookup_flags = LOOKUP_XATTR, error;
2666*7c478bd9Sstevel@tonic-gate 	int		exp_ro = 0;
2667*7c478bd9Sstevel@tonic-gate 
2668*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
2669*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2670*7c478bd9Sstevel@tonic-gate 		return;
2671*7c478bd9Sstevel@tonic-gate 	}
2672*7c478bd9Sstevel@tonic-gate 
2673*7c478bd9Sstevel@tonic-gate 	/*
2674*7c478bd9Sstevel@tonic-gate 	 * Make a couple of checks made by copen()
2675*7c478bd9Sstevel@tonic-gate 	 *
2676*7c478bd9Sstevel@tonic-gate 	 * Check to make sure underlying fs supports xattrs.  This
2677*7c478bd9Sstevel@tonic-gate 	 * is required because solaris filesystem implementations
2678*7c478bd9Sstevel@tonic-gate 	 * (UFS/TMPFS) don't enforce the noxattr mount option
2679*7c478bd9Sstevel@tonic-gate 	 * in VOP_LOOKUP(LOOKUP_XATTR).  If fs doesn't support this
2680*7c478bd9Sstevel@tonic-gate 	 * pathconf cmd or if fs supports cmd but doesn't claim
2681*7c478bd9Sstevel@tonic-gate 	 * support for xattr, return NOTSUPP.  It would be better
2682*7c478bd9Sstevel@tonic-gate 	 * to use VOP_PATHCONF( _PC_XATTR_ENABLED) for this; however,
2683*7c478bd9Sstevel@tonic-gate 	 * that cmd is not available to VOP_PATHCONF interface
2684*7c478bd9Sstevel@tonic-gate 	 * (it's only implemented inside pathconf syscall)...
2685*7c478bd9Sstevel@tonic-gate 	 *
2686*7c478bd9Sstevel@tonic-gate 	 * Verify permission to put attributes on files (access
2687*7c478bd9Sstevel@tonic-gate 	 * checks from copen).
2688*7c478bd9Sstevel@tonic-gate 	 */
2689*7c478bd9Sstevel@tonic-gate 
2690*7c478bd9Sstevel@tonic-gate 	if ((cs->vp->v_vfsp->vfs_flag & VFS_XATTR) == 0) {
2691*7c478bd9Sstevel@tonic-gate 		error = ENOTSUP;
2692*7c478bd9Sstevel@tonic-gate 		goto error_out;
2693*7c478bd9Sstevel@tonic-gate 	}
2694*7c478bd9Sstevel@tonic-gate 
2695*7c478bd9Sstevel@tonic-gate 	if ((VOP_ACCESS(cs->vp, VREAD, 0, cs->cr) != 0) &&
2696*7c478bd9Sstevel@tonic-gate 	    (VOP_ACCESS(cs->vp, VWRITE, 0, cs->cr) != 0) &&
2697*7c478bd9Sstevel@tonic-gate 	    (VOP_ACCESS(cs->vp, VEXEC, 0, cs->cr) != 0)) {
2698*7c478bd9Sstevel@tonic-gate 		error = EACCES;
2699*7c478bd9Sstevel@tonic-gate 		goto error_out;
2700*7c478bd9Sstevel@tonic-gate 	}
2701*7c478bd9Sstevel@tonic-gate 
2702*7c478bd9Sstevel@tonic-gate 	/*
2703*7c478bd9Sstevel@tonic-gate 	 * The CREATE_XATTR_DIR VOP flag cannot be specified if
2704*7c478bd9Sstevel@tonic-gate 	 * the file system is exported read-only -- regardless of
2705*7c478bd9Sstevel@tonic-gate 	 * createdir flag.  Otherwise the attrdir would be created
2706*7c478bd9Sstevel@tonic-gate 	 * (assuming server fs isn't mounted readonly locally).  If
2707*7c478bd9Sstevel@tonic-gate 	 * VOP_LOOKUP returns ENOENT in this case, the error will
2708*7c478bd9Sstevel@tonic-gate 	 * be translated into EROFS.  ENOSYS is mapped to ENOTSUP
2709*7c478bd9Sstevel@tonic-gate 	 * because specfs has no VOP_LOOKUP op, so the macro would
2710*7c478bd9Sstevel@tonic-gate 	 * return ENOSYS.  EINVAL is returned by all (current)
2711*7c478bd9Sstevel@tonic-gate 	 * Solaris file system implementations when any of their
2712*7c478bd9Sstevel@tonic-gate 	 * restrictions are violated (xattr(dir) can't have xattrdir).
2713*7c478bd9Sstevel@tonic-gate 	 * Returning NOTSUPP is more appropriate in this case
2714*7c478bd9Sstevel@tonic-gate 	 * because the object will never be able to have an attrdir.
2715*7c478bd9Sstevel@tonic-gate 	 */
2716*7c478bd9Sstevel@tonic-gate 	if (args->createdir && ! (exp_ro = rdonly4(cs->exi, cs->vp, req)))
2717*7c478bd9Sstevel@tonic-gate 		lookup_flags |= CREATE_XATTR_DIR;
2718*7c478bd9Sstevel@tonic-gate 
2719*7c478bd9Sstevel@tonic-gate 	error = VOP_LOOKUP(cs->vp, "", &avp, NULL, lookup_flags, NULL, cs->cr);
2720*7c478bd9Sstevel@tonic-gate 
2721*7c478bd9Sstevel@tonic-gate 	if (error) {
2722*7c478bd9Sstevel@tonic-gate 		if (error == ENOENT && args->createdir && exp_ro)
2723*7c478bd9Sstevel@tonic-gate 			error = EROFS;
2724*7c478bd9Sstevel@tonic-gate 		else if (error == EINVAL || error == ENOSYS)
2725*7c478bd9Sstevel@tonic-gate 			error = ENOTSUP;
2726*7c478bd9Sstevel@tonic-gate 		goto error_out;
2727*7c478bd9Sstevel@tonic-gate 	}
2728*7c478bd9Sstevel@tonic-gate 
2729*7c478bd9Sstevel@tonic-gate 	ASSERT(avp->v_flag & V_XATTRDIR);
2730*7c478bd9Sstevel@tonic-gate 
2731*7c478bd9Sstevel@tonic-gate 	error = makefh4(&cs->fh, avp, cs->exi);
2732*7c478bd9Sstevel@tonic-gate 
2733*7c478bd9Sstevel@tonic-gate 	if (error) {
2734*7c478bd9Sstevel@tonic-gate 		VN_RELE(avp);
2735*7c478bd9Sstevel@tonic-gate 		goto error_out;
2736*7c478bd9Sstevel@tonic-gate 	}
2737*7c478bd9Sstevel@tonic-gate 
2738*7c478bd9Sstevel@tonic-gate 	VN_RELE(cs->vp);
2739*7c478bd9Sstevel@tonic-gate 	cs->vp = avp;
2740*7c478bd9Sstevel@tonic-gate 
2741*7c478bd9Sstevel@tonic-gate 	/*
2742*7c478bd9Sstevel@tonic-gate 	 * There is no requirement for an attrdir fh flag
2743*7c478bd9Sstevel@tonic-gate 	 * because the attrdir has a vnode flag to distinguish
2744*7c478bd9Sstevel@tonic-gate 	 * it from regular (non-xattr) directories.  The
2745*7c478bd9Sstevel@tonic-gate 	 * FH4_ATTRDIR flag is set for future sanity checks.
2746*7c478bd9Sstevel@tonic-gate 	 */
2747*7c478bd9Sstevel@tonic-gate 	set_fh4_flag(&cs->fh, FH4_ATTRDIR);
2748*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
2749*7c478bd9Sstevel@tonic-gate 	return;
2750*7c478bd9Sstevel@tonic-gate 
2751*7c478bd9Sstevel@tonic-gate error_out:
2752*7c478bd9Sstevel@tonic-gate 
2753*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = puterrno4(error);
2754*7c478bd9Sstevel@tonic-gate }
2755*7c478bd9Sstevel@tonic-gate 
2756*7c478bd9Sstevel@tonic-gate static int
2757*7c478bd9Sstevel@tonic-gate do_io(int direction, vnode_t *vp, struct uio *uio, int ioflag, cred_t *cred)
2758*7c478bd9Sstevel@tonic-gate {
2759*7c478bd9Sstevel@tonic-gate 	int error;
2760*7c478bd9Sstevel@tonic-gate 	int i;
2761*7c478bd9Sstevel@tonic-gate 	clock_t delaytime;
2762*7c478bd9Sstevel@tonic-gate 	caller_context_t ct;
2763*7c478bd9Sstevel@tonic-gate 
2764*7c478bd9Sstevel@tonic-gate 	delaytime = MSEC_TO_TICK_ROUNDUP(rfs4_lock_delay);
2765*7c478bd9Sstevel@tonic-gate 
2766*7c478bd9Sstevel@tonic-gate 	/*
2767*7c478bd9Sstevel@tonic-gate 	 * Don't block on mandatory locks. If this routine returns
2768*7c478bd9Sstevel@tonic-gate 	 * EAGAIN, the caller should return NFS4ERR_LOCKED.
2769*7c478bd9Sstevel@tonic-gate 	 */
2770*7c478bd9Sstevel@tonic-gate 	uio->uio_fmode = FNONBLOCK;
2771*7c478bd9Sstevel@tonic-gate 
2772*7c478bd9Sstevel@tonic-gate 	ct.cc_sysid = 0;
2773*7c478bd9Sstevel@tonic-gate 	ct.cc_pid = 0;
2774*7c478bd9Sstevel@tonic-gate 	ct.cc_caller_id = nfs4_srv_caller_id;
2775*7c478bd9Sstevel@tonic-gate 
2776*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < rfs4_maxlock_tries; i++) {
2777*7c478bd9Sstevel@tonic-gate 
2778*7c478bd9Sstevel@tonic-gate 
2779*7c478bd9Sstevel@tonic-gate 		if (direction == FREAD) {
2780*7c478bd9Sstevel@tonic-gate 			(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &ct);
2781*7c478bd9Sstevel@tonic-gate 			error = VOP_READ(vp, uio, ioflag, cred, &ct);
2782*7c478bd9Sstevel@tonic-gate 			VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
2783*7c478bd9Sstevel@tonic-gate 		} else {
2784*7c478bd9Sstevel@tonic-gate 			(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &ct);
2785*7c478bd9Sstevel@tonic-gate 			error = VOP_WRITE(vp, uio, ioflag, cred, &ct);
2786*7c478bd9Sstevel@tonic-gate 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &ct);
2787*7c478bd9Sstevel@tonic-gate 		}
2788*7c478bd9Sstevel@tonic-gate 
2789*7c478bd9Sstevel@tonic-gate 		if (error != EAGAIN)
2790*7c478bd9Sstevel@tonic-gate 			break;
2791*7c478bd9Sstevel@tonic-gate 
2792*7c478bd9Sstevel@tonic-gate 		if (i < rfs4_maxlock_tries - 1) {
2793*7c478bd9Sstevel@tonic-gate 			delay(delaytime);
2794*7c478bd9Sstevel@tonic-gate 			delaytime *= 2;
2795*7c478bd9Sstevel@tonic-gate 		}
2796*7c478bd9Sstevel@tonic-gate 	}
2797*7c478bd9Sstevel@tonic-gate 
2798*7c478bd9Sstevel@tonic-gate 	return (error);
2799*7c478bd9Sstevel@tonic-gate }
2800*7c478bd9Sstevel@tonic-gate 
2801*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2802*7c478bd9Sstevel@tonic-gate static void
2803*7c478bd9Sstevel@tonic-gate rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
2804*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2805*7c478bd9Sstevel@tonic-gate {
2806*7c478bd9Sstevel@tonic-gate 	READ4args *args = &argop->nfs_argop4_u.opread;
2807*7c478bd9Sstevel@tonic-gate 	READ4res *resp = &resop->nfs_resop4_u.opread;
2808*7c478bd9Sstevel@tonic-gate 	int error;
2809*7c478bd9Sstevel@tonic-gate 	int verror;
2810*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
2811*7c478bd9Sstevel@tonic-gate 	struct vattr va;
2812*7c478bd9Sstevel@tonic-gate 	struct iovec iov;
2813*7c478bd9Sstevel@tonic-gate 	struct uio uio;
2814*7c478bd9Sstevel@tonic-gate 	u_offset_t offset;
2815*7c478bd9Sstevel@tonic-gate 	bool_t *deleg = &cs->deleg;
2816*7c478bd9Sstevel@tonic-gate 	nfsstat4 stat;
2817*7c478bd9Sstevel@tonic-gate 	int in_crit = 0;
2818*7c478bd9Sstevel@tonic-gate 	mblk_t *mp;
2819*7c478bd9Sstevel@tonic-gate 	int alloc_err = 0;
2820*7c478bd9Sstevel@tonic-gate 
2821*7c478bd9Sstevel@tonic-gate 	vp = cs->vp;
2822*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
2823*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
2824*7c478bd9Sstevel@tonic-gate 		return;
2825*7c478bd9Sstevel@tonic-gate 	}
2826*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
2827*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
2828*7c478bd9Sstevel@tonic-gate 		return;
2829*7c478bd9Sstevel@tonic-gate 	}
2830*7c478bd9Sstevel@tonic-gate 
2831*7c478bd9Sstevel@tonic-gate 	/*
2832*7c478bd9Sstevel@tonic-gate 	 * Enter the critical region before calling VOP_RWLOCK
2833*7c478bd9Sstevel@tonic-gate 	 * to avoid a deadlock with write requests.
2834*7c478bd9Sstevel@tonic-gate 	 */
2835*7c478bd9Sstevel@tonic-gate 	if (nbl_need_check(vp)) {
2836*7c478bd9Sstevel@tonic-gate 		nbl_start_crit(vp, RW_READER);
2837*7c478bd9Sstevel@tonic-gate 		in_crit = 1;
2838*7c478bd9Sstevel@tonic-gate 		if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0)) {
2839*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_LOCKED;
2840*7c478bd9Sstevel@tonic-gate 			goto out;
2841*7c478bd9Sstevel@tonic-gate 		}
2842*7c478bd9Sstevel@tonic-gate 	}
2843*7c478bd9Sstevel@tonic-gate 
2844*7c478bd9Sstevel@tonic-gate 	if ((stat = rfs4_check_stateid(FREAD, vp, &args->stateid, FALSE,
2845*7c478bd9Sstevel@tonic-gate 					deleg, TRUE)) != NFS4_OK) {
2846*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = stat;
2847*7c478bd9Sstevel@tonic-gate 		goto out;
2848*7c478bd9Sstevel@tonic-gate 	}
2849*7c478bd9Sstevel@tonic-gate 
2850*7c478bd9Sstevel@tonic-gate 	va.va_mask = AT_MODE|AT_SIZE|AT_UID;
2851*7c478bd9Sstevel@tonic-gate 	verror = VOP_GETATTR(vp, &va, 0, cs->cr);
2852*7c478bd9Sstevel@tonic-gate 
2853*7c478bd9Sstevel@tonic-gate 	/*
2854*7c478bd9Sstevel@tonic-gate 	 * If we can't get the attributes, then we can't do the
2855*7c478bd9Sstevel@tonic-gate 	 * right access checking.  So, we'll fail the request.
2856*7c478bd9Sstevel@tonic-gate 	 */
2857*7c478bd9Sstevel@tonic-gate 	if (verror) {
2858*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(verror);
2859*7c478bd9Sstevel@tonic-gate 		goto out;
2860*7c478bd9Sstevel@tonic-gate 	}
2861*7c478bd9Sstevel@tonic-gate 
2862*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VREG) {
2863*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status =
2864*7c478bd9Sstevel@tonic-gate 			((vp->v_type == VDIR) ? NFS4ERR_ISDIR : NFS4ERR_INVAL);
2865*7c478bd9Sstevel@tonic-gate 		goto out;
2866*7c478bd9Sstevel@tonic-gate 	}
2867*7c478bd9Sstevel@tonic-gate 
2868*7c478bd9Sstevel@tonic-gate 	if (crgetuid(cs->cr) != va.va_uid &&
2869*7c478bd9Sstevel@tonic-gate 	    (error = VOP_ACCESS(vp, VREAD, 0, cs->cr)) &&
2870*7c478bd9Sstevel@tonic-gate 	    (error = VOP_ACCESS(vp, VEXEC, 0, cs->cr))) {
2871*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
2872*7c478bd9Sstevel@tonic-gate 		goto out;
2873*7c478bd9Sstevel@tonic-gate 	}
2874*7c478bd9Sstevel@tonic-gate 
2875*7c478bd9Sstevel@tonic-gate 	if (MANDLOCK(vp, va.va_mode)) { /* XXX - V4 supports mand locking */
2876*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
2877*7c478bd9Sstevel@tonic-gate 		goto out;
2878*7c478bd9Sstevel@tonic-gate 	}
2879*7c478bd9Sstevel@tonic-gate 
2880*7c478bd9Sstevel@tonic-gate 	offset = args->offset;
2881*7c478bd9Sstevel@tonic-gate 	if (offset >= va.va_size) {
2882*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
2883*7c478bd9Sstevel@tonic-gate 		resp->eof = TRUE;
2884*7c478bd9Sstevel@tonic-gate 		resp->data_len = 0;
2885*7c478bd9Sstevel@tonic-gate 		resp->data_val = NULL;
2886*7c478bd9Sstevel@tonic-gate 		resp->mblk = NULL;
2887*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
2888*7c478bd9Sstevel@tonic-gate 		goto out;
2889*7c478bd9Sstevel@tonic-gate 	}
2890*7c478bd9Sstevel@tonic-gate 
2891*7c478bd9Sstevel@tonic-gate 	if (args->count == 0) {
2892*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
2893*7c478bd9Sstevel@tonic-gate 		resp->eof = FALSE;
2894*7c478bd9Sstevel@tonic-gate 		resp->data_len = 0;
2895*7c478bd9Sstevel@tonic-gate 		resp->data_val = NULL;
2896*7c478bd9Sstevel@tonic-gate 		resp->mblk = NULL;
2897*7c478bd9Sstevel@tonic-gate 		goto out;
2898*7c478bd9Sstevel@tonic-gate 	}
2899*7c478bd9Sstevel@tonic-gate 
2900*7c478bd9Sstevel@tonic-gate 	/*
2901*7c478bd9Sstevel@tonic-gate 	 * Do not allocate memory more than maximum allowed
2902*7c478bd9Sstevel@tonic-gate 	 * transfer size
2903*7c478bd9Sstevel@tonic-gate 	 */
2904*7c478bd9Sstevel@tonic-gate 	if (args->count > rfs4_tsize(req))
2905*7c478bd9Sstevel@tonic-gate 		args->count = rfs4_tsize(req);
2906*7c478bd9Sstevel@tonic-gate 
2907*7c478bd9Sstevel@tonic-gate 	/*
2908*7c478bd9Sstevel@tonic-gate 	 * mp will contain the data to be sent out in the read reply.
2909*7c478bd9Sstevel@tonic-gate 	 * It will be freed after the reply has been sent.
2910*7c478bd9Sstevel@tonic-gate 	 * Let's roundup the data to a BYTES_PER_XDR_UNIT multiple,
2911*7c478bd9Sstevel@tonic-gate 	 * so that the call to xdrmblk_putmblk() never fails.
2912*7c478bd9Sstevel@tonic-gate 	 * If the first alloc of the requested size fails, then
2913*7c478bd9Sstevel@tonic-gate 	 * decrease the size to something more reasonable and wait
2914*7c478bd9Sstevel@tonic-gate 	 * for the allocation to occur.
2915*7c478bd9Sstevel@tonic-gate 	 */
2916*7c478bd9Sstevel@tonic-gate 	mp = allocb(RNDUP(args->count), BPRI_MED);
2917*7c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
2918*7c478bd9Sstevel@tonic-gate 		if (args->count > MAXBSIZE)
2919*7c478bd9Sstevel@tonic-gate 			args->count = MAXBSIZE;
2920*7c478bd9Sstevel@tonic-gate 		mp = allocb_wait(RNDUP(args->count), BPRI_MED,
2921*7c478bd9Sstevel@tonic-gate 				STR_NOSIG, &alloc_err);
2922*7c478bd9Sstevel@tonic-gate 	}
2923*7c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
2924*7c478bd9Sstevel@tonic-gate 	ASSERT(alloc_err == 0);
2925*7c478bd9Sstevel@tonic-gate 
2926*7c478bd9Sstevel@tonic-gate 	iov.iov_base = (caddr_t)mp->b_datap->db_base;
2927*7c478bd9Sstevel@tonic-gate 	iov.iov_len = args->count;
2928*7c478bd9Sstevel@tonic-gate 	uio.uio_iov = &iov;
2929*7c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 1;
2930*7c478bd9Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
2931*7c478bd9Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_CACHED;
2932*7c478bd9Sstevel@tonic-gate 	uio.uio_loffset = args->offset;
2933*7c478bd9Sstevel@tonic-gate 	uio.uio_resid = args->count;
2934*7c478bd9Sstevel@tonic-gate 
2935*7c478bd9Sstevel@tonic-gate 	error = do_io(FREAD, vp, &uio, 0, cs->cr);
2936*7c478bd9Sstevel@tonic-gate 
2937*7c478bd9Sstevel@tonic-gate 	va.va_mask = AT_SIZE;
2938*7c478bd9Sstevel@tonic-gate 	verror = VOP_GETATTR(vp, &va, 0, cs->cr);
2939*7c478bd9Sstevel@tonic-gate 
2940*7c478bd9Sstevel@tonic-gate 	if (error) {
2941*7c478bd9Sstevel@tonic-gate 		freeb(mp);
2942*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
2943*7c478bd9Sstevel@tonic-gate 		goto out;
2944*7c478bd9Sstevel@tonic-gate 	}
2945*7c478bd9Sstevel@tonic-gate 
2946*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
2947*7c478bd9Sstevel@tonic-gate 
2948*7c478bd9Sstevel@tonic-gate 	ASSERT(uio.uio_resid >= 0);
2949*7c478bd9Sstevel@tonic-gate 	resp->data_len = args->count - uio.uio_resid;
2950*7c478bd9Sstevel@tonic-gate 	resp->data_val = (char *)mp->b_datap->db_base;
2951*7c478bd9Sstevel@tonic-gate 	resp->mblk = mp;
2952*7c478bd9Sstevel@tonic-gate 
2953*7c478bd9Sstevel@tonic-gate 	if (!verror && offset + resp->data_len == va.va_size)
2954*7c478bd9Sstevel@tonic-gate 		resp->eof = TRUE;
2955*7c478bd9Sstevel@tonic-gate 	else
2956*7c478bd9Sstevel@tonic-gate 		resp->eof = FALSE;
2957*7c478bd9Sstevel@tonic-gate 
2958*7c478bd9Sstevel@tonic-gate out:
2959*7c478bd9Sstevel@tonic-gate 	if (in_crit)
2960*7c478bd9Sstevel@tonic-gate 		nbl_end_crit(vp);
2961*7c478bd9Sstevel@tonic-gate }
2962*7c478bd9Sstevel@tonic-gate 
2963*7c478bd9Sstevel@tonic-gate static void
2964*7c478bd9Sstevel@tonic-gate rfs4_op_read_free(nfs_resop4 *resop)
2965*7c478bd9Sstevel@tonic-gate {
2966*7c478bd9Sstevel@tonic-gate 	READ4res *resp = &resop->nfs_resop4_u.opread;
2967*7c478bd9Sstevel@tonic-gate 
2968*7c478bd9Sstevel@tonic-gate 	if (resp->status == NFS4_OK && resp->mblk != NULL) {
2969*7c478bd9Sstevel@tonic-gate 		freeb(resp->mblk);
2970*7c478bd9Sstevel@tonic-gate 		resp->mblk = NULL;
2971*7c478bd9Sstevel@tonic-gate 		resp->data_val = NULL;
2972*7c478bd9Sstevel@tonic-gate 		resp->data_len = 0;
2973*7c478bd9Sstevel@tonic-gate 	}
2974*7c478bd9Sstevel@tonic-gate }
2975*7c478bd9Sstevel@tonic-gate 
2976*7c478bd9Sstevel@tonic-gate static void
2977*7c478bd9Sstevel@tonic-gate rfs4_op_readdir_free(nfs_resop4 *resop)
2978*7c478bd9Sstevel@tonic-gate {
2979*7c478bd9Sstevel@tonic-gate 	READDIR4res *resp = &resop->nfs_resop4_u.opreaddir;
2980*7c478bd9Sstevel@tonic-gate 
2981*7c478bd9Sstevel@tonic-gate 	if (resp->status == NFS4_OK && resp->mblk != NULL) {
2982*7c478bd9Sstevel@tonic-gate 		freeb(resp->mblk);
2983*7c478bd9Sstevel@tonic-gate 		resp->mblk = NULL;
2984*7c478bd9Sstevel@tonic-gate 		resp->data_len = 0;
2985*7c478bd9Sstevel@tonic-gate 	}
2986*7c478bd9Sstevel@tonic-gate }
2987*7c478bd9Sstevel@tonic-gate 
2988*7c478bd9Sstevel@tonic-gate 
2989*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2990*7c478bd9Sstevel@tonic-gate static void
2991*7c478bd9Sstevel@tonic-gate rfs4_op_putpubfh(nfs_argop4 *args, nfs_resop4 *resop, struct svc_req *req,
2992*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
2993*7c478bd9Sstevel@tonic-gate {
2994*7c478bd9Sstevel@tonic-gate 	PUTPUBFH4res *resp = &resop->nfs_resop4_u.opputpubfh;
2995*7c478bd9Sstevel@tonic-gate 	int error;
2996*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
2997*7c478bd9Sstevel@tonic-gate 	struct exportinfo *exi, *sav_exi;
2998*7c478bd9Sstevel@tonic-gate 	nfs_fh4_fmt_t *fh_fmtp;
2999*7c478bd9Sstevel@tonic-gate 
3000*7c478bd9Sstevel@tonic-gate 	if (cs->vp) {
3001*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
3002*7c478bd9Sstevel@tonic-gate 		cs->vp = NULL;
3003*7c478bd9Sstevel@tonic-gate 	}
3004*7c478bd9Sstevel@tonic-gate 
3005*7c478bd9Sstevel@tonic-gate 	if (cs->cr)
3006*7c478bd9Sstevel@tonic-gate 		crfree(cs->cr);
3007*7c478bd9Sstevel@tonic-gate 
3008*7c478bd9Sstevel@tonic-gate 	cs->cr = crdup(cs->basecr);
3009*7c478bd9Sstevel@tonic-gate 
3010*7c478bd9Sstevel@tonic-gate 	vp = exi_public->exi_vp;
3011*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
3012*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_SERVERFAULT;
3013*7c478bd9Sstevel@tonic-gate 		return;
3014*7c478bd9Sstevel@tonic-gate 	}
3015*7c478bd9Sstevel@tonic-gate 
3016*7c478bd9Sstevel@tonic-gate 	error = makefh4(&cs->fh, vp, exi_public);
3017*7c478bd9Sstevel@tonic-gate 	if (error != 0) {
3018*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3019*7c478bd9Sstevel@tonic-gate 		return;
3020*7c478bd9Sstevel@tonic-gate 	}
3021*7c478bd9Sstevel@tonic-gate 	sav_exi = cs->exi;
3022*7c478bd9Sstevel@tonic-gate 	if (exi_public == exi_root) {
3023*7c478bd9Sstevel@tonic-gate 		/*
3024*7c478bd9Sstevel@tonic-gate 		 * No filesystem is actually shared public, so we default
3025*7c478bd9Sstevel@tonic-gate 		 * to exi_root. In this case, we must check whether root
3026*7c478bd9Sstevel@tonic-gate 		 * is exported.
3027*7c478bd9Sstevel@tonic-gate 		 */
3028*7c478bd9Sstevel@tonic-gate 		fh_fmtp = (nfs_fh4_fmt_t *)cs->fh.nfs_fh4_val;
3029*7c478bd9Sstevel@tonic-gate 
3030*7c478bd9Sstevel@tonic-gate 		/*
3031*7c478bd9Sstevel@tonic-gate 		 * if root filesystem is exported, the exportinfo struct that we
3032*7c478bd9Sstevel@tonic-gate 		 * should use is what checkexport4 returns, because root_exi is
3033*7c478bd9Sstevel@tonic-gate 		 * actually a mostly empty struct.
3034*7c478bd9Sstevel@tonic-gate 		 */
3035*7c478bd9Sstevel@tonic-gate 		exi = checkexport4(&fh_fmtp->fh4_fsid,
3036*7c478bd9Sstevel@tonic-gate 			(fid_t *)&fh_fmtp->fh4_xlen, NULL);
3037*7c478bd9Sstevel@tonic-gate 		cs->exi = ((exi != NULL) ? exi : exi_public);
3038*7c478bd9Sstevel@tonic-gate 	} else {
3039*7c478bd9Sstevel@tonic-gate 		/*
3040*7c478bd9Sstevel@tonic-gate 		 * it's a properly shared filesystem
3041*7c478bd9Sstevel@tonic-gate 		 */
3042*7c478bd9Sstevel@tonic-gate 		cs->exi = exi_public;
3043*7c478bd9Sstevel@tonic-gate 	}
3044*7c478bd9Sstevel@tonic-gate 
3045*7c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
3046*7c478bd9Sstevel@tonic-gate 	cs->vp = vp;
3047*7c478bd9Sstevel@tonic-gate 
3048*7c478bd9Sstevel@tonic-gate 	if ((resp->status = call_checkauth4(cs, req)) != NFS4_OK) {
3049*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
3050*7c478bd9Sstevel@tonic-gate 		cs->vp = NULL;
3051*7c478bd9Sstevel@tonic-gate 		cs->exi = sav_exi;
3052*7c478bd9Sstevel@tonic-gate 		return;
3053*7c478bd9Sstevel@tonic-gate 	}
3054*7c478bd9Sstevel@tonic-gate 
3055*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3056*7c478bd9Sstevel@tonic-gate }
3057*7c478bd9Sstevel@tonic-gate 
3058*7c478bd9Sstevel@tonic-gate /*
3059*7c478bd9Sstevel@tonic-gate  * XXX - issue with put*fh operations. Suppose /export/home is exported.
3060*7c478bd9Sstevel@tonic-gate  * Suppose an NFS client goes to mount /export/home/joe. If /export, home,
3061*7c478bd9Sstevel@tonic-gate  * or joe have restrictive search permissions, then we shouldn't let
3062*7c478bd9Sstevel@tonic-gate  * the client get a file handle. This is easy to enforce. However, we
3063*7c478bd9Sstevel@tonic-gate  * don't know what security flavor should be used until we resolve the
3064*7c478bd9Sstevel@tonic-gate  * path name. Another complication is uid mapping. If root is
3065*7c478bd9Sstevel@tonic-gate  * the user, then it will be mapped to the anonymous user by default,
3066*7c478bd9Sstevel@tonic-gate  * but we won't know that till we've resolved the path name. And we won't
3067*7c478bd9Sstevel@tonic-gate  * know what the anonymous user is.
3068*7c478bd9Sstevel@tonic-gate  * Luckily, SECINFO is specified to take a full filename.
3069*7c478bd9Sstevel@tonic-gate  * So what we will have to in rfs4_op_lookup is check that flavor of
3070*7c478bd9Sstevel@tonic-gate  * the target object matches that of the request, and if root was the
3071*7c478bd9Sstevel@tonic-gate  * caller, check for the root= and anon= options, and if necessary,
3072*7c478bd9Sstevel@tonic-gate  * repeat the lookup using the right cred_t. But that's not done yet.
3073*7c478bd9Sstevel@tonic-gate  */
3074*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3075*7c478bd9Sstevel@tonic-gate static void
3076*7c478bd9Sstevel@tonic-gate rfs4_op_putfh(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
3077*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
3078*7c478bd9Sstevel@tonic-gate {
3079*7c478bd9Sstevel@tonic-gate 	PUTFH4args *args = &argop->nfs_argop4_u.opputfh;
3080*7c478bd9Sstevel@tonic-gate 	PUTFH4res *resp = &resop->nfs_resop4_u.opputfh;
3081*7c478bd9Sstevel@tonic-gate 	nfs_fh4_fmt_t *fh_fmtp;
3082*7c478bd9Sstevel@tonic-gate 
3083*7c478bd9Sstevel@tonic-gate 	if (cs->vp) {
3084*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
3085*7c478bd9Sstevel@tonic-gate 		cs->vp = NULL;
3086*7c478bd9Sstevel@tonic-gate 	}
3087*7c478bd9Sstevel@tonic-gate 
3088*7c478bd9Sstevel@tonic-gate 	if (cs->cr) {
3089*7c478bd9Sstevel@tonic-gate 		crfree(cs->cr);
3090*7c478bd9Sstevel@tonic-gate 		cs->cr = NULL;
3091*7c478bd9Sstevel@tonic-gate 	}
3092*7c478bd9Sstevel@tonic-gate 
3093*7c478bd9Sstevel@tonic-gate 
3094*7c478bd9Sstevel@tonic-gate 	if (args->object.nfs_fh4_len < NFS_FH4_LEN) {
3095*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
3096*7c478bd9Sstevel@tonic-gate 		return;
3097*7c478bd9Sstevel@tonic-gate 	}
3098*7c478bd9Sstevel@tonic-gate 
3099*7c478bd9Sstevel@tonic-gate 	fh_fmtp = (nfs_fh4_fmt_t *)args->object.nfs_fh4_val;
3100*7c478bd9Sstevel@tonic-gate 	cs->exi = checkexport4(&fh_fmtp->fh4_fsid, (fid_t *)&fh_fmtp->fh4_xlen,
3101*7c478bd9Sstevel@tonic-gate 				NULL);
3102*7c478bd9Sstevel@tonic-gate 
3103*7c478bd9Sstevel@tonic-gate 	if (cs->exi == NULL) {
3104*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_STALE;
3105*7c478bd9Sstevel@tonic-gate 		return;
3106*7c478bd9Sstevel@tonic-gate 	}
3107*7c478bd9Sstevel@tonic-gate 
3108*7c478bd9Sstevel@tonic-gate 	cs->cr = crdup(cs->basecr);
3109*7c478bd9Sstevel@tonic-gate 
3110*7c478bd9Sstevel@tonic-gate 	ASSERT(cs->cr != NULL);
3111*7c478bd9Sstevel@tonic-gate 
3112*7c478bd9Sstevel@tonic-gate 	if (! (cs->vp = nfs4_fhtovp(&args->object, cs->exi, &resp->status))) {
3113*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status;
3114*7c478bd9Sstevel@tonic-gate 		return;
3115*7c478bd9Sstevel@tonic-gate 	}
3116*7c478bd9Sstevel@tonic-gate 
3117*7c478bd9Sstevel@tonic-gate 	if ((resp->status = call_checkauth4(cs, req)) != NFS4_OK) {
3118*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
3119*7c478bd9Sstevel@tonic-gate 		cs->vp = NULL;
3120*7c478bd9Sstevel@tonic-gate 		return;
3121*7c478bd9Sstevel@tonic-gate 	}
3122*7c478bd9Sstevel@tonic-gate 
3123*7c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(&args->object, &cs->fh);
3124*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3125*7c478bd9Sstevel@tonic-gate 	cs->deleg = FALSE;
3126*7c478bd9Sstevel@tonic-gate }
3127*7c478bd9Sstevel@tonic-gate 
3128*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3129*7c478bd9Sstevel@tonic-gate static void
3130*7c478bd9Sstevel@tonic-gate rfs4_op_putrootfh(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
3131*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
3132*7c478bd9Sstevel@tonic-gate 
3133*7c478bd9Sstevel@tonic-gate {
3134*7c478bd9Sstevel@tonic-gate 	PUTROOTFH4res *resp = &resop->nfs_resop4_u.opputrootfh;
3135*7c478bd9Sstevel@tonic-gate 	int error;
3136*7c478bd9Sstevel@tonic-gate 	fid_t fid;
3137*7c478bd9Sstevel@tonic-gate 	struct exportinfo *exi, *sav_exi;
3138*7c478bd9Sstevel@tonic-gate 
3139*7c478bd9Sstevel@tonic-gate 	if (cs->vp) {
3140*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
3141*7c478bd9Sstevel@tonic-gate 		cs->vp = NULL;
3142*7c478bd9Sstevel@tonic-gate 	}
3143*7c478bd9Sstevel@tonic-gate 
3144*7c478bd9Sstevel@tonic-gate 	if (cs->cr)
3145*7c478bd9Sstevel@tonic-gate 		crfree(cs->cr);
3146*7c478bd9Sstevel@tonic-gate 
3147*7c478bd9Sstevel@tonic-gate 	cs->cr = crdup(cs->basecr);
3148*7c478bd9Sstevel@tonic-gate 
3149*7c478bd9Sstevel@tonic-gate 	/*
3150*7c478bd9Sstevel@tonic-gate 	 * Using rootdir, the system root vnode,
3151*7c478bd9Sstevel@tonic-gate 	 * get its fid.
3152*7c478bd9Sstevel@tonic-gate 	 */
3153*7c478bd9Sstevel@tonic-gate 	bzero(&fid, sizeof (fid));
3154*7c478bd9Sstevel@tonic-gate 	fid.fid_len = MAXFIDSZ;
3155*7c478bd9Sstevel@tonic-gate 	error = vop_fid_pseudo(rootdir, &fid);
3156*7c478bd9Sstevel@tonic-gate 	if (error != 0) {
3157*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3158*7c478bd9Sstevel@tonic-gate 		return;
3159*7c478bd9Sstevel@tonic-gate 	}
3160*7c478bd9Sstevel@tonic-gate 
3161*7c478bd9Sstevel@tonic-gate 	/*
3162*7c478bd9Sstevel@tonic-gate 	 * Then use the root fsid & fid it to find out if it's exported
3163*7c478bd9Sstevel@tonic-gate 	 *
3164*7c478bd9Sstevel@tonic-gate 	 * If the server root isn't exported directly, then
3165*7c478bd9Sstevel@tonic-gate 	 * it should at least be a pseudo export based on
3166*7c478bd9Sstevel@tonic-gate 	 * one or more exports further down in the server's
3167*7c478bd9Sstevel@tonic-gate 	 * file tree.
3168*7c478bd9Sstevel@tonic-gate 	 */
3169*7c478bd9Sstevel@tonic-gate 	exi = checkexport4(&rootdir->v_vfsp->vfs_fsid, &fid, NULL);
3170*7c478bd9Sstevel@tonic-gate 	if (exi == NULL || exi->exi_export.ex_flags & EX_PUBLIC) {
3171*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
3172*7c478bd9Sstevel@tonic-gate 			(CE_WARN, "rfs4_op_putrootfh: export check failure"));
3173*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_SERVERFAULT;
3174*7c478bd9Sstevel@tonic-gate 		return;
3175*7c478bd9Sstevel@tonic-gate 	}
3176*7c478bd9Sstevel@tonic-gate 
3177*7c478bd9Sstevel@tonic-gate 	/*
3178*7c478bd9Sstevel@tonic-gate 	 * Now make a filehandle based on the root
3179*7c478bd9Sstevel@tonic-gate 	 * export and root vnode.
3180*7c478bd9Sstevel@tonic-gate 	 */
3181*7c478bd9Sstevel@tonic-gate 	error = makefh4(&cs->fh, rootdir, exi);
3182*7c478bd9Sstevel@tonic-gate 	if (error != 0) {
3183*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3184*7c478bd9Sstevel@tonic-gate 		return;
3185*7c478bd9Sstevel@tonic-gate 	}
3186*7c478bd9Sstevel@tonic-gate 
3187*7c478bd9Sstevel@tonic-gate 	sav_exi = cs->exi;
3188*7c478bd9Sstevel@tonic-gate 	cs->exi = exi;
3189*7c478bd9Sstevel@tonic-gate 
3190*7c478bd9Sstevel@tonic-gate 	VN_HOLD(rootdir);
3191*7c478bd9Sstevel@tonic-gate 	cs->vp = rootdir;
3192*7c478bd9Sstevel@tonic-gate 
3193*7c478bd9Sstevel@tonic-gate 	if ((resp->status = call_checkauth4(cs, req)) != NFS4_OK) {
3194*7c478bd9Sstevel@tonic-gate 		VN_RELE(rootdir);
3195*7c478bd9Sstevel@tonic-gate 		cs->vp = NULL;
3196*7c478bd9Sstevel@tonic-gate 		cs->exi = sav_exi;
3197*7c478bd9Sstevel@tonic-gate 		return;
3198*7c478bd9Sstevel@tonic-gate 	}
3199*7c478bd9Sstevel@tonic-gate 
3200*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3201*7c478bd9Sstevel@tonic-gate 	cs->deleg = FALSE;
3202*7c478bd9Sstevel@tonic-gate }
3203*7c478bd9Sstevel@tonic-gate 
3204*7c478bd9Sstevel@tonic-gate /*
3205*7c478bd9Sstevel@tonic-gate  * A directory entry is a valid nfsv4 entry if
3206*7c478bd9Sstevel@tonic-gate  * - it has a non-zero ino
3207*7c478bd9Sstevel@tonic-gate  * - it is not a dot or dotdot name
3208*7c478bd9Sstevel@tonic-gate  * - it is visible in a pseudo export or in a real export that can
3209*7c478bd9Sstevel@tonic-gate  *   only have a limited view.
3210*7c478bd9Sstevel@tonic-gate  */
3211*7c478bd9Sstevel@tonic-gate static bool_t
3212*7c478bd9Sstevel@tonic-gate valid_nfs4_entry(struct exportinfo *exi, struct dirent64 *dp,
3213*7c478bd9Sstevel@tonic-gate 		int *expseudo, int check_visible)
3214*7c478bd9Sstevel@tonic-gate {
3215*7c478bd9Sstevel@tonic-gate 	if (dp->d_ino == 0 || NFS_IS_DOTNAME(dp->d_name)) {
3216*7c478bd9Sstevel@tonic-gate 		*expseudo = 0;
3217*7c478bd9Sstevel@tonic-gate 		return (FALSE);
3218*7c478bd9Sstevel@tonic-gate 	}
3219*7c478bd9Sstevel@tonic-gate 
3220*7c478bd9Sstevel@tonic-gate 	if (! check_visible) {
3221*7c478bd9Sstevel@tonic-gate 		*expseudo = 0;
3222*7c478bd9Sstevel@tonic-gate 		return (TRUE);
3223*7c478bd9Sstevel@tonic-gate 	}
3224*7c478bd9Sstevel@tonic-gate 
3225*7c478bd9Sstevel@tonic-gate 	return (nfs_visible_inode(exi, dp->d_ino, expseudo));
3226*7c478bd9Sstevel@tonic-gate }
3227*7c478bd9Sstevel@tonic-gate 
3228*7c478bd9Sstevel@tonic-gate /*
3229*7c478bd9Sstevel@tonic-gate  * set_rdattr_params sets up the variables used to manage what information
3230*7c478bd9Sstevel@tonic-gate  * to get for each directory entry.
3231*7c478bd9Sstevel@tonic-gate  */
3232*7c478bd9Sstevel@tonic-gate static nfsstat4
3233*7c478bd9Sstevel@tonic-gate set_rdattr_params(struct nfs4_svgetit_arg *sargp,
3234*7c478bd9Sstevel@tonic-gate 		bitmap4 attrs, bool_t *need_to_lookup)
3235*7c478bd9Sstevel@tonic-gate {
3236*7c478bd9Sstevel@tonic-gate 	uint_t	va_mask;
3237*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
3238*7c478bd9Sstevel@tonic-gate 	bitmap4 objbits;
3239*7c478bd9Sstevel@tonic-gate 
3240*7c478bd9Sstevel@tonic-gate 	status = bitmap4_to_attrmask(attrs, sargp);
3241*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
3242*7c478bd9Sstevel@tonic-gate 		/*
3243*7c478bd9Sstevel@tonic-gate 		 * could not even figure attr mask
3244*7c478bd9Sstevel@tonic-gate 		 */
3245*7c478bd9Sstevel@tonic-gate 		return (status);
3246*7c478bd9Sstevel@tonic-gate 	}
3247*7c478bd9Sstevel@tonic-gate 	va_mask = sargp->vap->va_mask;
3248*7c478bd9Sstevel@tonic-gate 
3249*7c478bd9Sstevel@tonic-gate 	/*
3250*7c478bd9Sstevel@tonic-gate 	 * dirent's d_ino is always correct value for mounted_on_fileid.
3251*7c478bd9Sstevel@tonic-gate 	 * mntdfid_set is set once here, but mounted_on_fileid is
3252*7c478bd9Sstevel@tonic-gate 	 * set in main dirent processing loop for each dirent.
3253*7c478bd9Sstevel@tonic-gate 	 * The mntdfid_set is a simple optimization that lets the
3254*7c478bd9Sstevel@tonic-gate 	 * server attr code avoid work when caller is readdir.
3255*7c478bd9Sstevel@tonic-gate 	 */
3256*7c478bd9Sstevel@tonic-gate 	sargp->mntdfid_set = TRUE;
3257*7c478bd9Sstevel@tonic-gate 
3258*7c478bd9Sstevel@tonic-gate 	/*
3259*7c478bd9Sstevel@tonic-gate 	 * Lookup entry only if client asked for any of the following:
3260*7c478bd9Sstevel@tonic-gate 	 * a) vattr attrs
3261*7c478bd9Sstevel@tonic-gate 	 * b) vfs attrs
3262*7c478bd9Sstevel@tonic-gate 	 * c) attrs w/per-object scope requested (change, filehandle, etc)
3263*7c478bd9Sstevel@tonic-gate 	 *    other than mounted_on_fileid (which we can take from dirent)
3264*7c478bd9Sstevel@tonic-gate 	 */
3265*7c478bd9Sstevel@tonic-gate 	objbits = attrs ? attrs & NFS4_VP_ATTR_MASK : 0;
3266*7c478bd9Sstevel@tonic-gate 
3267*7c478bd9Sstevel@tonic-gate 	if (va_mask || sargp->sbp || (objbits & ~FATTR4_MOUNTED_ON_FILEID_MASK))
3268*7c478bd9Sstevel@tonic-gate 		*need_to_lookup = TRUE;
3269*7c478bd9Sstevel@tonic-gate 	else
3270*7c478bd9Sstevel@tonic-gate 		*need_to_lookup = FALSE;
3271*7c478bd9Sstevel@tonic-gate 
3272*7c478bd9Sstevel@tonic-gate 	if (sargp->sbp == NULL)
3273*7c478bd9Sstevel@tonic-gate 		return (NFS4_OK);
3274*7c478bd9Sstevel@tonic-gate 
3275*7c478bd9Sstevel@tonic-gate 	/*
3276*7c478bd9Sstevel@tonic-gate 	 * If filesystem attrs are requested, get them now from the
3277*7c478bd9Sstevel@tonic-gate 	 * directory vp, as most entries will have same filesystem. The only
3278*7c478bd9Sstevel@tonic-gate 	 * exception are mounted over entries but we handle
3279*7c478bd9Sstevel@tonic-gate 	 * those as we go (XXX mounted over detection not yet implemented).
3280*7c478bd9Sstevel@tonic-gate 	 */
3281*7c478bd9Sstevel@tonic-gate 	sargp->vap->va_mask = 0;	/* to avoid VOP_GETATTR */
3282*7c478bd9Sstevel@tonic-gate 	status = bitmap4_get_sysattrs(sargp);
3283*7c478bd9Sstevel@tonic-gate 	sargp->vap->va_mask = va_mask;
3284*7c478bd9Sstevel@tonic-gate 
3285*7c478bd9Sstevel@tonic-gate 	if ((status != NFS4_OK) && sargp->rdattr_error_req) {
3286*7c478bd9Sstevel@tonic-gate 		/*
3287*7c478bd9Sstevel@tonic-gate 		 * Failed to get filesystem attributes.
3288*7c478bd9Sstevel@tonic-gate 		 * Return a rdattr_error for each entry, but don't fail.
3289*7c478bd9Sstevel@tonic-gate 		 * However, don't get any obj-dependent attrs.
3290*7c478bd9Sstevel@tonic-gate 		 */
3291*7c478bd9Sstevel@tonic-gate 		sargp->rdattr_error = status;	/* for rdattr_error */
3292*7c478bd9Sstevel@tonic-gate 		*need_to_lookup = FALSE;
3293*7c478bd9Sstevel@tonic-gate 		/*
3294*7c478bd9Sstevel@tonic-gate 		 * At least get fileid for regular readdir output
3295*7c478bd9Sstevel@tonic-gate 		 */
3296*7c478bd9Sstevel@tonic-gate 		sargp->vap->va_mask &= AT_NODEID;
3297*7c478bd9Sstevel@tonic-gate 		status = NFS4_OK;
3298*7c478bd9Sstevel@tonic-gate 	}
3299*7c478bd9Sstevel@tonic-gate 
3300*7c478bd9Sstevel@tonic-gate 	return (status);
3301*7c478bd9Sstevel@tonic-gate }
3302*7c478bd9Sstevel@tonic-gate 
3303*7c478bd9Sstevel@tonic-gate /*
3304*7c478bd9Sstevel@tonic-gate  * readlink: args: CURRENT_FH.
3305*7c478bd9Sstevel@tonic-gate  *	res: status. If success - CURRENT_FH unchanged, return linktext.
3306*7c478bd9Sstevel@tonic-gate  */
3307*7c478bd9Sstevel@tonic-gate 
3308*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3309*7c478bd9Sstevel@tonic-gate static void
3310*7c478bd9Sstevel@tonic-gate rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
3311*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
3312*7c478bd9Sstevel@tonic-gate {
3313*7c478bd9Sstevel@tonic-gate 	READLINK4res *resp = &resop->nfs_resop4_u.opreadlink;
3314*7c478bd9Sstevel@tonic-gate 	int error;
3315*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
3316*7c478bd9Sstevel@tonic-gate 	struct iovec iov;
3317*7c478bd9Sstevel@tonic-gate 	struct vattr va;
3318*7c478bd9Sstevel@tonic-gate 	struct uio uio;
3319*7c478bd9Sstevel@tonic-gate 	char *data;
3320*7c478bd9Sstevel@tonic-gate 
3321*7c478bd9Sstevel@tonic-gate 	/* CURRENT_FH: directory */
3322*7c478bd9Sstevel@tonic-gate 	vp = cs->vp;
3323*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
3324*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
3325*7c478bd9Sstevel@tonic-gate 		return;
3326*7c478bd9Sstevel@tonic-gate 	}
3327*7c478bd9Sstevel@tonic-gate 
3328*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
3329*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3330*7c478bd9Sstevel@tonic-gate 		return;
3331*7c478bd9Sstevel@tonic-gate 	}
3332*7c478bd9Sstevel@tonic-gate 
3333*7c478bd9Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
3334*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ISDIR;
3335*7c478bd9Sstevel@tonic-gate 		return;
3336*7c478bd9Sstevel@tonic-gate 	}
3337*7c478bd9Sstevel@tonic-gate 
3338*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VLNK) {
3339*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3340*7c478bd9Sstevel@tonic-gate 		return;
3341*7c478bd9Sstevel@tonic-gate 	}
3342*7c478bd9Sstevel@tonic-gate 
3343*7c478bd9Sstevel@tonic-gate 	va.va_mask = AT_MODE;
3344*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(vp, &va, 0, cs->cr);
3345*7c478bd9Sstevel@tonic-gate 	if (error) {
3346*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3347*7c478bd9Sstevel@tonic-gate 		return;
3348*7c478bd9Sstevel@tonic-gate 	}
3349*7c478bd9Sstevel@tonic-gate 
3350*7c478bd9Sstevel@tonic-gate 	if (MANDLOCK(vp, va.va_mode)) {
3351*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3352*7c478bd9Sstevel@tonic-gate 		return;
3353*7c478bd9Sstevel@tonic-gate 	}
3354*7c478bd9Sstevel@tonic-gate 
3355*7c478bd9Sstevel@tonic-gate 	data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP);
3356*7c478bd9Sstevel@tonic-gate 
3357*7c478bd9Sstevel@tonic-gate 	iov.iov_base = data;
3358*7c478bd9Sstevel@tonic-gate 	iov.iov_len = MAXPATHLEN;
3359*7c478bd9Sstevel@tonic-gate 	uio.uio_iov = &iov;
3360*7c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 1;
3361*7c478bd9Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
3362*7c478bd9Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_CACHED;
3363*7c478bd9Sstevel@tonic-gate 	uio.uio_loffset = 0;
3364*7c478bd9Sstevel@tonic-gate 	uio.uio_resid = MAXPATHLEN;
3365*7c478bd9Sstevel@tonic-gate 
3366*7c478bd9Sstevel@tonic-gate 	error = VOP_READLINK(vp, &uio, cs->cr);
3367*7c478bd9Sstevel@tonic-gate 
3368*7c478bd9Sstevel@tonic-gate 	if (error) {
3369*7c478bd9Sstevel@tonic-gate 		kmem_free((caddr_t)data, (uint_t)MAXPATHLEN + 1);
3370*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3371*7c478bd9Sstevel@tonic-gate 		return;
3372*7c478bd9Sstevel@tonic-gate 	}
3373*7c478bd9Sstevel@tonic-gate 
3374*7c478bd9Sstevel@tonic-gate 	*(data + MAXPATHLEN - uio.uio_resid) = '\0';
3375*7c478bd9Sstevel@tonic-gate 
3376*7c478bd9Sstevel@tonic-gate 	/*
3377*7c478bd9Sstevel@tonic-gate 	 * treat link name as data
3378*7c478bd9Sstevel@tonic-gate 	 */
3379*7c478bd9Sstevel@tonic-gate 	(void) str_to_utf8(data, &resp->link);
3380*7c478bd9Sstevel@tonic-gate 
3381*7c478bd9Sstevel@tonic-gate 	kmem_free((caddr_t)data, (uint_t)MAXPATHLEN + 1);
3382*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3383*7c478bd9Sstevel@tonic-gate }
3384*7c478bd9Sstevel@tonic-gate 
3385*7c478bd9Sstevel@tonic-gate static void
3386*7c478bd9Sstevel@tonic-gate rfs4_op_readlink_free(nfs_resop4 *resop)
3387*7c478bd9Sstevel@tonic-gate {
3388*7c478bd9Sstevel@tonic-gate 	READLINK4res *resp = &resop->nfs_resop4_u.opreadlink;
3389*7c478bd9Sstevel@tonic-gate 	utf8string *symlink = &resp->link;
3390*7c478bd9Sstevel@tonic-gate 
3391*7c478bd9Sstevel@tonic-gate 	if (symlink->utf8string_val) {
3392*7c478bd9Sstevel@tonic-gate 		UTF8STRING_FREE(*symlink)
3393*7c478bd9Sstevel@tonic-gate 	}
3394*7c478bd9Sstevel@tonic-gate }
3395*7c478bd9Sstevel@tonic-gate 
3396*7c478bd9Sstevel@tonic-gate /*
3397*7c478bd9Sstevel@tonic-gate  * release_lockowner:
3398*7c478bd9Sstevel@tonic-gate  *	Release any state associated with the supplied
3399*7c478bd9Sstevel@tonic-gate  *	lockowner. Note if any lo_state is holding locks we will not
3400*7c478bd9Sstevel@tonic-gate  *	rele that lo_state and thus the lockowner will not be destroyed.
3401*7c478bd9Sstevel@tonic-gate  *	A client using lock after the lock owner stateid has been released
3402*7c478bd9Sstevel@tonic-gate  *	will suffer the consequence of NFS4ERR_BAD_STATEID and would have
3403*7c478bd9Sstevel@tonic-gate  *	to reissue the lock with new_lock_owner set to TRUE.
3404*7c478bd9Sstevel@tonic-gate  *	args: lock_owner
3405*7c478bd9Sstevel@tonic-gate  *	res:  status
3406*7c478bd9Sstevel@tonic-gate  */
3407*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3408*7c478bd9Sstevel@tonic-gate static void
3409*7c478bd9Sstevel@tonic-gate rfs4_op_release_lockowner(nfs_argop4 *argop, nfs_resop4 *resop,
3410*7c478bd9Sstevel@tonic-gate 	struct svc_req *req, struct compound_state *cs)
3411*7c478bd9Sstevel@tonic-gate {
3412*7c478bd9Sstevel@tonic-gate 	RELEASE_LOCKOWNER4args *ap = &argop->nfs_argop4_u.oprelease_lockowner;
3413*7c478bd9Sstevel@tonic-gate 	RELEASE_LOCKOWNER4res *resp = &resop->nfs_resop4_u.oprelease_lockowner;
3414*7c478bd9Sstevel@tonic-gate 	rfs4_lockowner_t *lo;
3415*7c478bd9Sstevel@tonic-gate 	rfs4_openowner_t *oop;
3416*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *sp;
3417*7c478bd9Sstevel@tonic-gate 	rfs4_lo_state_t *lsp;
3418*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp;
3419*7c478bd9Sstevel@tonic-gate 	bool_t create = FALSE;
3420*7c478bd9Sstevel@tonic-gate 	locklist_t *llist;
3421*7c478bd9Sstevel@tonic-gate 	sysid_t sysid;
3422*7c478bd9Sstevel@tonic-gate 
3423*7c478bd9Sstevel@tonic-gate 	/* Make sure there is a clientid around for this request */
3424*7c478bd9Sstevel@tonic-gate 	cp = rfs4_findclient_by_id(ap->lock_owner.clientid, FALSE);
3425*7c478bd9Sstevel@tonic-gate 
3426*7c478bd9Sstevel@tonic-gate 	if (cp == NULL) {
3427*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status =
3428*7c478bd9Sstevel@tonic-gate 			rfs4_check_clientid(&ap->lock_owner.clientid, 0);
3429*7c478bd9Sstevel@tonic-gate 		return;
3430*7c478bd9Sstevel@tonic-gate 	}
3431*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(cp);
3432*7c478bd9Sstevel@tonic-gate 
3433*7c478bd9Sstevel@tonic-gate 	lo = rfs4_findlockowner(&ap->lock_owner, &create);
3434*7c478bd9Sstevel@tonic-gate 	if (lo == NULL) {
3435*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
3436*7c478bd9Sstevel@tonic-gate 		return;
3437*7c478bd9Sstevel@tonic-gate 	}
3438*7c478bd9Sstevel@tonic-gate 	ASSERT(lo->client != NULL);
3439*7c478bd9Sstevel@tonic-gate 
3440*7c478bd9Sstevel@tonic-gate 	/*
3441*7c478bd9Sstevel@tonic-gate 	 * Check for EXPIRED client. If so will reap state with in a lease
3442*7c478bd9Sstevel@tonic-gate 	 * period or on next set_clientid_confirm step
3443*7c478bd9Sstevel@tonic-gate 	 */
3444*7c478bd9Sstevel@tonic-gate 	if (rfs4_lease_expired(lo->client)) {
3445*7c478bd9Sstevel@tonic-gate 		rfs4_lockowner_rele(lo);
3446*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
3447*7c478bd9Sstevel@tonic-gate 		return;
3448*7c478bd9Sstevel@tonic-gate 	}
3449*7c478bd9Sstevel@tonic-gate 
3450*7c478bd9Sstevel@tonic-gate 	/*
3451*7c478bd9Sstevel@tonic-gate 	 * If no sysid has been assigned, then no locks exist; just return.
3452*7c478bd9Sstevel@tonic-gate 	 */
3453*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(lo->client->dbe);
3454*7c478bd9Sstevel@tonic-gate 	if (lo->client->sysidt == LM_NOSYSID) {
3455*7c478bd9Sstevel@tonic-gate 		rfs4_lockowner_rele(lo);
3456*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(lo->client->dbe);
3457*7c478bd9Sstevel@tonic-gate 		return;
3458*7c478bd9Sstevel@tonic-gate 	}
3459*7c478bd9Sstevel@tonic-gate 
3460*7c478bd9Sstevel@tonic-gate 	sysid = lo->client->sysidt;
3461*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(lo->client->dbe);
3462*7c478bd9Sstevel@tonic-gate 
3463*7c478bd9Sstevel@tonic-gate 	/*
3464*7c478bd9Sstevel@tonic-gate 	 * Mark the lockowner invalid.
3465*7c478bd9Sstevel@tonic-gate 	 */
3466*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_hide(lo->dbe);
3467*7c478bd9Sstevel@tonic-gate 
3468*7c478bd9Sstevel@tonic-gate 	/*
3469*7c478bd9Sstevel@tonic-gate 	 * sysid-pid pair should now not be used since the lockowner is
3470*7c478bd9Sstevel@tonic-gate 	 * invalid. If the client were to instantiate the lockowner again
3471*7c478bd9Sstevel@tonic-gate 	 * it would be assigned a new pid. Thus we can get the list of
3472*7c478bd9Sstevel@tonic-gate 	 * current locks.
3473*7c478bd9Sstevel@tonic-gate 	 */
3474*7c478bd9Sstevel@tonic-gate 
3475*7c478bd9Sstevel@tonic-gate 	llist = flk_get_active_locks(sysid, lo->pid);
3476*7c478bd9Sstevel@tonic-gate 	/* If we are still holding locks fail */
3477*7c478bd9Sstevel@tonic-gate 	if (llist != NULL) {
3478*7c478bd9Sstevel@tonic-gate 
3479*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_LOCKS_HELD;
3480*7c478bd9Sstevel@tonic-gate 
3481*7c478bd9Sstevel@tonic-gate 		flk_free_locklist(llist);
3482*7c478bd9Sstevel@tonic-gate 		/*
3483*7c478bd9Sstevel@tonic-gate 		 * We need to unhide the lockowner so the client can
3484*7c478bd9Sstevel@tonic-gate 		 * try it again. The bad thing here is if the client
3485*7c478bd9Sstevel@tonic-gate 		 * has a logic error that took it here in the first place
3486*7c478bd9Sstevel@tonic-gate 		 * he probably has lost accounting of the locks that it
3487*7c478bd9Sstevel@tonic-gate 		 * is holding. So we may have dangling state until the
3488*7c478bd9Sstevel@tonic-gate 		 * open owner state is reaped via close. One scenario
3489*7c478bd9Sstevel@tonic-gate 		 * that could possibly occur is that the client has
3490*7c478bd9Sstevel@tonic-gate 		 * sent the unlock request(s) in separate threads
3491*7c478bd9Sstevel@tonic-gate 		 * and has not waited for the replies before sending the
3492*7c478bd9Sstevel@tonic-gate 		 * RELEASE_LOCKOWNER request. Presumably, it would expect
3493*7c478bd9Sstevel@tonic-gate 		 * and deal appropriately with NFS4ERR_LOCKS_HELD, by
3494*7c478bd9Sstevel@tonic-gate 		 * reissuing the request.
3495*7c478bd9Sstevel@tonic-gate 		 */
3496*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unhide(lo->dbe);
3497*7c478bd9Sstevel@tonic-gate 		rfs4_lockowner_rele(lo);
3498*7c478bd9Sstevel@tonic-gate 		return;
3499*7c478bd9Sstevel@tonic-gate 	}
3500*7c478bd9Sstevel@tonic-gate 
3501*7c478bd9Sstevel@tonic-gate 	/*
3502*7c478bd9Sstevel@tonic-gate 	 * For the corresponding client we need to check each open
3503*7c478bd9Sstevel@tonic-gate 	 * owner for any opens that have lockowner state associated
3504*7c478bd9Sstevel@tonic-gate 	 * with this lockowner.
3505*7c478bd9Sstevel@tonic-gate 	 */
3506*7c478bd9Sstevel@tonic-gate 
3507*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(lo->client->dbe);
3508*7c478bd9Sstevel@tonic-gate 	for (oop = lo->client->openownerlist.next->oop; oop != NULL;
3509*7c478bd9Sstevel@tonic-gate 	    oop = oop->openownerlist.next->oop) {
3510*7c478bd9Sstevel@tonic-gate 
3511*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(oop->dbe);
3512*7c478bd9Sstevel@tonic-gate 		for (sp = oop->ownerstateids.next->sp; sp != NULL;
3513*7c478bd9Sstevel@tonic-gate 		    sp = sp->ownerstateids.next->sp) {
3514*7c478bd9Sstevel@tonic-gate 
3515*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_lock(sp->dbe);
3516*7c478bd9Sstevel@tonic-gate 			for (lsp = sp->lockownerlist.next->lsp;
3517*7c478bd9Sstevel@tonic-gate 			    lsp != NULL; lsp = lsp->lockownerlist.next->lsp) {
3518*7c478bd9Sstevel@tonic-gate 				if (lsp->locker == lo) {
3519*7c478bd9Sstevel@tonic-gate 					rfs4_dbe_lock(lsp->dbe);
3520*7c478bd9Sstevel@tonic-gate 					rfs4_dbe_invalidate(lsp->dbe);
3521*7c478bd9Sstevel@tonic-gate 					rfs4_dbe_unlock(lsp->dbe);
3522*7c478bd9Sstevel@tonic-gate 				}
3523*7c478bd9Sstevel@tonic-gate 			}
3524*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_unlock(sp->dbe);
3525*7c478bd9Sstevel@tonic-gate 		}
3526*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(oop->dbe);
3527*7c478bd9Sstevel@tonic-gate 	}
3528*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(lo->client->dbe);
3529*7c478bd9Sstevel@tonic-gate 
3530*7c478bd9Sstevel@tonic-gate 	rfs4_lockowner_rele(lo);
3531*7c478bd9Sstevel@tonic-gate 
3532*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3533*7c478bd9Sstevel@tonic-gate }
3534*7c478bd9Sstevel@tonic-gate 
3535*7c478bd9Sstevel@tonic-gate /*
3536*7c478bd9Sstevel@tonic-gate  * short utility function to lookup a file and recall the delegation
3537*7c478bd9Sstevel@tonic-gate  */
3538*7c478bd9Sstevel@tonic-gate static rfs4_file_t *
3539*7c478bd9Sstevel@tonic-gate rfs4_lookup_and_findfile(vnode_t *dvp, char *nm, vnode_t **vpp,
3540*7c478bd9Sstevel@tonic-gate 	int *lkup_error, cred_t *cr)
3541*7c478bd9Sstevel@tonic-gate {
3542*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
3543*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = NULL;
3544*7c478bd9Sstevel@tonic-gate 	bool_t fcreate = FALSE;
3545*7c478bd9Sstevel@tonic-gate 	int error;
3546*7c478bd9Sstevel@tonic-gate 
3547*7c478bd9Sstevel@tonic-gate 	if (vpp)
3548*7c478bd9Sstevel@tonic-gate 		*vpp = NULL;
3549*7c478bd9Sstevel@tonic-gate 
3550*7c478bd9Sstevel@tonic-gate 	if ((error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cr)) == 0) {
3551*7c478bd9Sstevel@tonic-gate 		VN_SETPATH(rootdir, dvp, vp, nm, strlen(nm));
3552*7c478bd9Sstevel@tonic-gate 		if (vp->v_type == VREG)
3553*7c478bd9Sstevel@tonic-gate 			fp = rfs4_findfile(vp, NULL, &fcreate);
3554*7c478bd9Sstevel@tonic-gate 		if (vpp)
3555*7c478bd9Sstevel@tonic-gate 			*vpp = vp;
3556*7c478bd9Sstevel@tonic-gate 		else
3557*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
3558*7c478bd9Sstevel@tonic-gate 	}
3559*7c478bd9Sstevel@tonic-gate 
3560*7c478bd9Sstevel@tonic-gate 	if (lkup_error)
3561*7c478bd9Sstevel@tonic-gate 		*lkup_error = error;
3562*7c478bd9Sstevel@tonic-gate 
3563*7c478bd9Sstevel@tonic-gate 	return (fp);
3564*7c478bd9Sstevel@tonic-gate }
3565*7c478bd9Sstevel@tonic-gate 
3566*7c478bd9Sstevel@tonic-gate /*
3567*7c478bd9Sstevel@tonic-gate  * remove: args: CURRENT_FH: directory; name.
3568*7c478bd9Sstevel@tonic-gate  *	res: status. If success - CURRENT_FH unchanged, return change_info
3569*7c478bd9Sstevel@tonic-gate  *		for directory.
3570*7c478bd9Sstevel@tonic-gate  */
3571*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3572*7c478bd9Sstevel@tonic-gate static void
3573*7c478bd9Sstevel@tonic-gate rfs4_op_remove(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
3574*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
3575*7c478bd9Sstevel@tonic-gate {
3576*7c478bd9Sstevel@tonic-gate 	REMOVE4args *args = &argop->nfs_argop4_u.opremove;
3577*7c478bd9Sstevel@tonic-gate 	REMOVE4res *resp = &resop->nfs_resop4_u.opremove;
3578*7c478bd9Sstevel@tonic-gate 	int error;
3579*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp, *vp;
3580*7c478bd9Sstevel@tonic-gate 	struct vattr bdva, idva, adva;
3581*7c478bd9Sstevel@tonic-gate 	char *nm;
3582*7c478bd9Sstevel@tonic-gate 	uint_t len;
3583*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp;
3584*7c478bd9Sstevel@tonic-gate 	int in_crit = 0;
3585*7c478bd9Sstevel@tonic-gate 
3586*7c478bd9Sstevel@tonic-gate 	/* CURRENT_FH: directory */
3587*7c478bd9Sstevel@tonic-gate 	dvp = cs->vp;
3588*7c478bd9Sstevel@tonic-gate 	if (dvp == NULL) {
3589*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
3590*7c478bd9Sstevel@tonic-gate 		return;
3591*7c478bd9Sstevel@tonic-gate 	}
3592*7c478bd9Sstevel@tonic-gate 
3593*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
3594*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3595*7c478bd9Sstevel@tonic-gate 		return;
3596*7c478bd9Sstevel@tonic-gate 	}
3597*7c478bd9Sstevel@tonic-gate 
3598*7c478bd9Sstevel@tonic-gate 	/*
3599*7c478bd9Sstevel@tonic-gate 	 * If there is an unshared filesystem mounted on this vnode,
3600*7c478bd9Sstevel@tonic-gate 	 * Do not allow to remove anything in this directory.
3601*7c478bd9Sstevel@tonic-gate 	 */
3602*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(dvp)) {
3603*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3604*7c478bd9Sstevel@tonic-gate 		return;
3605*7c478bd9Sstevel@tonic-gate 	}
3606*7c478bd9Sstevel@tonic-gate 
3607*7c478bd9Sstevel@tonic-gate 	if (dvp->v_type != VDIR) {
3608*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
3609*7c478bd9Sstevel@tonic-gate 		return;
3610*7c478bd9Sstevel@tonic-gate 	}
3611*7c478bd9Sstevel@tonic-gate 
3612*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(&args->target)) {
3613*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3614*7c478bd9Sstevel@tonic-gate 		return;
3615*7c478bd9Sstevel@tonic-gate 	}
3616*7c478bd9Sstevel@tonic-gate 
3617*7c478bd9Sstevel@tonic-gate 	/*
3618*7c478bd9Sstevel@tonic-gate 	 * Lookup the file so that we can check if it's a directory
3619*7c478bd9Sstevel@tonic-gate 	 */
3620*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_fn(&args->target, &len, NULL);
3621*7c478bd9Sstevel@tonic-gate 	if (nm == NULL) {
3622*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3623*7c478bd9Sstevel@tonic-gate 		return;
3624*7c478bd9Sstevel@tonic-gate 	}
3625*7c478bd9Sstevel@tonic-gate 
3626*7c478bd9Sstevel@tonic-gate 	if (len > MAXNAMELEN) {
3627*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
3628*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
3629*7c478bd9Sstevel@tonic-gate 		return;
3630*7c478bd9Sstevel@tonic-gate 	}
3631*7c478bd9Sstevel@tonic-gate 
3632*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
3633*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
3634*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
3635*7c478bd9Sstevel@tonic-gate 		return;
3636*7c478bd9Sstevel@tonic-gate 	}
3637*7c478bd9Sstevel@tonic-gate 
3638*7c478bd9Sstevel@tonic-gate 	/*
3639*7c478bd9Sstevel@tonic-gate 	 * Lookup the file to determine type and while we are see if
3640*7c478bd9Sstevel@tonic-gate 	 * there is a file struct around and check for delegation.
3641*7c478bd9Sstevel@tonic-gate 	 * We don't need to acquire va_seq before this lookup, if
3642*7c478bd9Sstevel@tonic-gate 	 * it causes an update, cinfo.before will not match, which will
3643*7c478bd9Sstevel@tonic-gate 	 * trigger a cache flush even if atomic is TRUE.
3644*7c478bd9Sstevel@tonic-gate 	 */
3645*7c478bd9Sstevel@tonic-gate 	if (fp = rfs4_lookup_and_findfile(dvp, nm, &vp, &error, cs->cr)) {
3646*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_delegated_byfp(FWRITE, fp, TRUE, TRUE, TRUE,
3647*7c478bd9Sstevel@tonic-gate 						NULL)) {
3648*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
3649*7c478bd9Sstevel@tonic-gate 			rfs4_file_rele(fp);
3650*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_DELAY;
3651*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, len);
3652*7c478bd9Sstevel@tonic-gate 			return;
3653*7c478bd9Sstevel@tonic-gate 		}
3654*7c478bd9Sstevel@tonic-gate 	}
3655*7c478bd9Sstevel@tonic-gate 
3656*7c478bd9Sstevel@tonic-gate 	/* Didn't find anything to remove */
3657*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
3658*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = error;
3659*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
3660*7c478bd9Sstevel@tonic-gate 		return;
3661*7c478bd9Sstevel@tonic-gate 	}
3662*7c478bd9Sstevel@tonic-gate 
3663*7c478bd9Sstevel@tonic-gate 	if (nbl_need_check(vp)) {
3664*7c478bd9Sstevel@tonic-gate 		nbl_start_crit(vp, RW_READER);
3665*7c478bd9Sstevel@tonic-gate 		in_crit = 1;
3666*7c478bd9Sstevel@tonic-gate 		if (nbl_conflict(vp, NBL_REMOVE, 0, 0, 0)) {
3667*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_FILE_OPEN;
3668*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, len);
3669*7c478bd9Sstevel@tonic-gate 			nbl_end_crit(vp);
3670*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
3671*7c478bd9Sstevel@tonic-gate 			if (fp) {
3672*7c478bd9Sstevel@tonic-gate 				rfs4_clear_dont_grant(fp);
3673*7c478bd9Sstevel@tonic-gate 				rfs4_file_rele(fp);
3674*7c478bd9Sstevel@tonic-gate 			}
3675*7c478bd9Sstevel@tonic-gate 			return;
3676*7c478bd9Sstevel@tonic-gate 		}
3677*7c478bd9Sstevel@tonic-gate 	}
3678*7c478bd9Sstevel@tonic-gate 
3679*7c478bd9Sstevel@tonic-gate 	/* Get dir "before" change value */
3680*7c478bd9Sstevel@tonic-gate 	bdva.va_mask = AT_CTIME|AT_SEQ;
3681*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(dvp, &bdva, 0, cs->cr);
3682*7c478bd9Sstevel@tonic-gate 	if (error) {
3683*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3684*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
3685*7c478bd9Sstevel@tonic-gate 		return;
3686*7c478bd9Sstevel@tonic-gate 	}
3687*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->cinfo.before, bdva.va_ctime)
3688*7c478bd9Sstevel@tonic-gate 
3689*7c478bd9Sstevel@tonic-gate 	/* Actually do the REMOVE operation */
3690*7c478bd9Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
3691*7c478bd9Sstevel@tonic-gate 		/*
3692*7c478bd9Sstevel@tonic-gate 		 * Can't remove a directory that has a mounted-on filesystem.
3693*7c478bd9Sstevel@tonic-gate 		 */
3694*7c478bd9Sstevel@tonic-gate 		if (vn_ismntpt(vp)) {
3695*7c478bd9Sstevel@tonic-gate 			error = EACCES;
3696*7c478bd9Sstevel@tonic-gate 		} else {
3697*7c478bd9Sstevel@tonic-gate 			/*
3698*7c478bd9Sstevel@tonic-gate 			 * System V defines rmdir to return EEXIST,
3699*7c478bd9Sstevel@tonic-gate 			 * not * ENOTEMPTY, if the directory is not
3700*7c478bd9Sstevel@tonic-gate 			 * empty.  A System V NFS server needs to map
3701*7c478bd9Sstevel@tonic-gate 			 * NFS4ERR_EXIST to NFS4ERR_NOTEMPTY to
3702*7c478bd9Sstevel@tonic-gate 			 * transmit over the wire.
3703*7c478bd9Sstevel@tonic-gate 			 */
3704*7c478bd9Sstevel@tonic-gate 			if ((error = VOP_RMDIR(dvp, nm, rootdir, cs->cr))
3705*7c478bd9Sstevel@tonic-gate 				== EEXIST)
3706*7c478bd9Sstevel@tonic-gate 				error = ENOTEMPTY;
3707*7c478bd9Sstevel@tonic-gate 		}
3708*7c478bd9Sstevel@tonic-gate 	} else {
3709*7c478bd9Sstevel@tonic-gate 		if ((error = VOP_REMOVE(dvp, nm, cs->cr)) == 0 &&
3710*7c478bd9Sstevel@tonic-gate 			fp != NULL) {
3711*7c478bd9Sstevel@tonic-gate 			struct vattr va;
3712*7c478bd9Sstevel@tonic-gate 
3713*7c478bd9Sstevel@tonic-gate 			/*
3714*7c478bd9Sstevel@tonic-gate 			 * This is va_seq safe because we are not
3715*7c478bd9Sstevel@tonic-gate 			 * manipulating dvp.
3716*7c478bd9Sstevel@tonic-gate 			 */
3717*7c478bd9Sstevel@tonic-gate 			va.va_mask = AT_NLINK;
3718*7c478bd9Sstevel@tonic-gate 			if (!VOP_GETATTR(fp->vp, &va, 0, cs->cr) &&
3719*7c478bd9Sstevel@tonic-gate 				va.va_nlink == 0) {
3720*7c478bd9Sstevel@tonic-gate 				/* The file is gone and so should the state */
3721*7c478bd9Sstevel@tonic-gate 				if (in_crit) {
3722*7c478bd9Sstevel@tonic-gate 					nbl_end_crit(vp);
3723*7c478bd9Sstevel@tonic-gate 					in_crit = 0;
3724*7c478bd9Sstevel@tonic-gate 				}
3725*7c478bd9Sstevel@tonic-gate 				rfs4_close_all_state(fp);
3726*7c478bd9Sstevel@tonic-gate 			}
3727*7c478bd9Sstevel@tonic-gate 		}
3728*7c478bd9Sstevel@tonic-gate 	}
3729*7c478bd9Sstevel@tonic-gate 
3730*7c478bd9Sstevel@tonic-gate 	if (in_crit)
3731*7c478bd9Sstevel@tonic-gate 		nbl_end_crit(vp);
3732*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
3733*7c478bd9Sstevel@tonic-gate 
3734*7c478bd9Sstevel@tonic-gate 	if (fp) {
3735*7c478bd9Sstevel@tonic-gate 		rfs4_clear_dont_grant(fp);
3736*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(fp);
3737*7c478bd9Sstevel@tonic-gate 	}
3738*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, len);
3739*7c478bd9Sstevel@tonic-gate 
3740*7c478bd9Sstevel@tonic-gate 	if (error) {
3741*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3742*7c478bd9Sstevel@tonic-gate 		return;
3743*7c478bd9Sstevel@tonic-gate 	}
3744*7c478bd9Sstevel@tonic-gate 
3745*7c478bd9Sstevel@tonic-gate 	/*
3746*7c478bd9Sstevel@tonic-gate 	 * Get the initial "after" sequence number, if it fails, set to zero
3747*7c478bd9Sstevel@tonic-gate 	 */
3748*7c478bd9Sstevel@tonic-gate 	idva.va_mask = AT_SEQ;
3749*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &idva, 0, cs->cr))
3750*7c478bd9Sstevel@tonic-gate 		idva.va_seq = 0;
3751*7c478bd9Sstevel@tonic-gate 
3752*7c478bd9Sstevel@tonic-gate 	/*
3753*7c478bd9Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
3754*7c478bd9Sstevel@tonic-gate 	 */
3755*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(dvp, 0, cs->cr);
3756*7c478bd9Sstevel@tonic-gate 
3757*7c478bd9Sstevel@tonic-gate 	/*
3758*7c478bd9Sstevel@tonic-gate 	 * Get "after" change value, if it fails, simply return the
3759*7c478bd9Sstevel@tonic-gate 	 * before value.
3760*7c478bd9Sstevel@tonic-gate 	 */
3761*7c478bd9Sstevel@tonic-gate 	adva.va_mask = AT_CTIME|AT_SEQ;
3762*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &adva, 0, cs->cr)) {
3763*7c478bd9Sstevel@tonic-gate 		adva.va_ctime = bdva.va_ctime;
3764*7c478bd9Sstevel@tonic-gate 		adva.va_seq = 0;
3765*7c478bd9Sstevel@tonic-gate 	}
3766*7c478bd9Sstevel@tonic-gate 
3767*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->cinfo.after, adva.va_ctime)
3768*7c478bd9Sstevel@tonic-gate 
3769*7c478bd9Sstevel@tonic-gate 	/*
3770*7c478bd9Sstevel@tonic-gate 	 * The cinfo.atomic = TRUE only if we have
3771*7c478bd9Sstevel@tonic-gate 	 * non-zero va_seq's, and it has incremented by exactly one
3772*7c478bd9Sstevel@tonic-gate 	 * during the VOP_REMOVE/RMDIR and it didn't change during
3773*7c478bd9Sstevel@tonic-gate 	 * the VOP_FSYNC.
3774*7c478bd9Sstevel@tonic-gate 	 */
3775*7c478bd9Sstevel@tonic-gate 	if (bdva.va_seq && idva.va_seq && adva.va_seq &&
3776*7c478bd9Sstevel@tonic-gate 			idva.va_seq == (bdva.va_seq + 1) &&
3777*7c478bd9Sstevel@tonic-gate 			idva.va_seq == adva.va_seq)
3778*7c478bd9Sstevel@tonic-gate 		resp->cinfo.atomic = TRUE;
3779*7c478bd9Sstevel@tonic-gate 	else
3780*7c478bd9Sstevel@tonic-gate 		resp->cinfo.atomic = FALSE;
3781*7c478bd9Sstevel@tonic-gate 
3782*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
3783*7c478bd9Sstevel@tonic-gate }
3784*7c478bd9Sstevel@tonic-gate 
3785*7c478bd9Sstevel@tonic-gate /*
3786*7c478bd9Sstevel@tonic-gate  * rename: args: SAVED_FH: from directory, CURRENT_FH: target directory,
3787*7c478bd9Sstevel@tonic-gate  *		oldname and newname.
3788*7c478bd9Sstevel@tonic-gate  *	res: status. If success - CURRENT_FH unchanged, return change_info
3789*7c478bd9Sstevel@tonic-gate  *		for both from and target directories.
3790*7c478bd9Sstevel@tonic-gate  */
3791*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
3792*7c478bd9Sstevel@tonic-gate static void
3793*7c478bd9Sstevel@tonic-gate rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
3794*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
3795*7c478bd9Sstevel@tonic-gate {
3796*7c478bd9Sstevel@tonic-gate 	RENAME4args *args = &argop->nfs_argop4_u.oprename;
3797*7c478bd9Sstevel@tonic-gate 	RENAME4res *resp = &resop->nfs_resop4_u.oprename;
3798*7c478bd9Sstevel@tonic-gate 	int error;
3799*7c478bd9Sstevel@tonic-gate 	vnode_t *odvp;
3800*7c478bd9Sstevel@tonic-gate 	vnode_t *ndvp;
3801*7c478bd9Sstevel@tonic-gate 	vnode_t *srcvp, *targvp;
3802*7c478bd9Sstevel@tonic-gate 	struct vattr obdva, oidva, oadva;
3803*7c478bd9Sstevel@tonic-gate 	struct vattr nbdva, nidva, nadva;
3804*7c478bd9Sstevel@tonic-gate 	char *onm, *nnm;
3805*7c478bd9Sstevel@tonic-gate 	uint_t olen, nlen;
3806*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp, *sfp;
3807*7c478bd9Sstevel@tonic-gate 	int in_crit_src, in_crit_targ;
3808*7c478bd9Sstevel@tonic-gate 	int fp_rele_grant_hold, sfp_rele_grant_hold;
3809*7c478bd9Sstevel@tonic-gate 
3810*7c478bd9Sstevel@tonic-gate 	fp = sfp = NULL;
3811*7c478bd9Sstevel@tonic-gate 	srcvp = targvp = NULL;
3812*7c478bd9Sstevel@tonic-gate 	in_crit_src = in_crit_targ = 0;
3813*7c478bd9Sstevel@tonic-gate 	fp_rele_grant_hold = sfp_rele_grant_hold = 0;
3814*7c478bd9Sstevel@tonic-gate 
3815*7c478bd9Sstevel@tonic-gate 	/* CURRENT_FH: target directory */
3816*7c478bd9Sstevel@tonic-gate 	ndvp = cs->vp;
3817*7c478bd9Sstevel@tonic-gate 	if (ndvp == NULL) {
3818*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
3819*7c478bd9Sstevel@tonic-gate 		return;
3820*7c478bd9Sstevel@tonic-gate 	}
3821*7c478bd9Sstevel@tonic-gate 
3822*7c478bd9Sstevel@tonic-gate 	/* SAVED_FH: from directory */
3823*7c478bd9Sstevel@tonic-gate 	odvp = cs->saved_vp;
3824*7c478bd9Sstevel@tonic-gate 	if (odvp == NULL) {
3825*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
3826*7c478bd9Sstevel@tonic-gate 		return;
3827*7c478bd9Sstevel@tonic-gate 	}
3828*7c478bd9Sstevel@tonic-gate 
3829*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
3830*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3831*7c478bd9Sstevel@tonic-gate 		return;
3832*7c478bd9Sstevel@tonic-gate 	}
3833*7c478bd9Sstevel@tonic-gate 
3834*7c478bd9Sstevel@tonic-gate 	/*
3835*7c478bd9Sstevel@tonic-gate 	 * If there is an unshared filesystem mounted on this vnode,
3836*7c478bd9Sstevel@tonic-gate 	 * do not allow to rename objects in this directory.
3837*7c478bd9Sstevel@tonic-gate 	 */
3838*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(odvp)) {
3839*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3840*7c478bd9Sstevel@tonic-gate 		return;
3841*7c478bd9Sstevel@tonic-gate 	}
3842*7c478bd9Sstevel@tonic-gate 
3843*7c478bd9Sstevel@tonic-gate 	/*
3844*7c478bd9Sstevel@tonic-gate 	 * If there is an unshared filesystem mounted on this vnode,
3845*7c478bd9Sstevel@tonic-gate 	 * do not allow to rename to this directory.
3846*7c478bd9Sstevel@tonic-gate 	 */
3847*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(ndvp)) {
3848*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
3849*7c478bd9Sstevel@tonic-gate 		return;
3850*7c478bd9Sstevel@tonic-gate 	}
3851*7c478bd9Sstevel@tonic-gate 
3852*7c478bd9Sstevel@tonic-gate 	if (odvp->v_type != VDIR || ndvp->v_type != VDIR) {
3853*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOTDIR;
3854*7c478bd9Sstevel@tonic-gate 		return;
3855*7c478bd9Sstevel@tonic-gate 	}
3856*7c478bd9Sstevel@tonic-gate 
3857*7c478bd9Sstevel@tonic-gate 	if (cs->saved_exi != cs->exi) {
3858*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_XDEV;
3859*7c478bd9Sstevel@tonic-gate 		return;
3860*7c478bd9Sstevel@tonic-gate 	}
3861*7c478bd9Sstevel@tonic-gate 
3862*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(&args->oldname)) {
3863*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3864*7c478bd9Sstevel@tonic-gate 		return;
3865*7c478bd9Sstevel@tonic-gate 	}
3866*7c478bd9Sstevel@tonic-gate 
3867*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(&args->newname)) {
3868*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3869*7c478bd9Sstevel@tonic-gate 		return;
3870*7c478bd9Sstevel@tonic-gate 	}
3871*7c478bd9Sstevel@tonic-gate 
3872*7c478bd9Sstevel@tonic-gate 	onm = utf8_to_fn(&args->oldname, &olen, NULL);
3873*7c478bd9Sstevel@tonic-gate 	if (onm == NULL) {
3874*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3875*7c478bd9Sstevel@tonic-gate 		return;
3876*7c478bd9Sstevel@tonic-gate 	}
3877*7c478bd9Sstevel@tonic-gate 
3878*7c478bd9Sstevel@tonic-gate 	nnm = utf8_to_fn(&args->newname, &nlen, NULL);
3879*7c478bd9Sstevel@tonic-gate 	if (nnm == NULL) {
3880*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
3881*7c478bd9Sstevel@tonic-gate 		kmem_free(onm, olen);
3882*7c478bd9Sstevel@tonic-gate 		return;
3883*7c478bd9Sstevel@tonic-gate 	}
3884*7c478bd9Sstevel@tonic-gate 
3885*7c478bd9Sstevel@tonic-gate 	if (olen > MAXNAMELEN || nlen > MAXNAMELEN) {
3886*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NAMETOOLONG;
3887*7c478bd9Sstevel@tonic-gate 		kmem_free(onm, olen);
3888*7c478bd9Sstevel@tonic-gate 		kmem_free(nnm, nlen);
3889*7c478bd9Sstevel@tonic-gate 		return;
3890*7c478bd9Sstevel@tonic-gate 	}
3891*7c478bd9Sstevel@tonic-gate 
3892*7c478bd9Sstevel@tonic-gate 
3893*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
3894*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
3895*7c478bd9Sstevel@tonic-gate 		kmem_free(onm, olen);
3896*7c478bd9Sstevel@tonic-gate 		kmem_free(nnm, nlen);
3897*7c478bd9Sstevel@tonic-gate 		return;
3898*7c478bd9Sstevel@tonic-gate 	}
3899*7c478bd9Sstevel@tonic-gate 
3900*7c478bd9Sstevel@tonic-gate 	/*
3901*7c478bd9Sstevel@tonic-gate 	 * Is the source a file and have a delegation?
3902*7c478bd9Sstevel@tonic-gate 	 * We don't need to acquire va_seq before these lookups, if
3903*7c478bd9Sstevel@tonic-gate 	 * it causes an update, cinfo.before will not match, which will
3904*7c478bd9Sstevel@tonic-gate 	 * trigger a cache flush even if atomic is TRUE.
3905*7c478bd9Sstevel@tonic-gate 	 */
3906*7c478bd9Sstevel@tonic-gate 	if (sfp = rfs4_lookup_and_findfile(odvp, onm, &srcvp, &error, cs->cr)) {
3907*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_delegated_byfp(FWRITE, sfp, TRUE, TRUE, TRUE,
3908*7c478bd9Sstevel@tonic-gate 						NULL)) {
3909*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_DELAY;
3910*7c478bd9Sstevel@tonic-gate 			goto err_out;
3911*7c478bd9Sstevel@tonic-gate 		}
3912*7c478bd9Sstevel@tonic-gate 	}
3913*7c478bd9Sstevel@tonic-gate 
3914*7c478bd9Sstevel@tonic-gate 	if (srcvp == NULL) {
3915*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3916*7c478bd9Sstevel@tonic-gate 		kmem_free(onm, olen);
3917*7c478bd9Sstevel@tonic-gate 		kmem_free(nnm, nlen);
3918*7c478bd9Sstevel@tonic-gate 		return;
3919*7c478bd9Sstevel@tonic-gate 	}
3920*7c478bd9Sstevel@tonic-gate 
3921*7c478bd9Sstevel@tonic-gate 	sfp_rele_grant_hold = 1;
3922*7c478bd9Sstevel@tonic-gate 
3923*7c478bd9Sstevel@tonic-gate 	/* Does the destination exist and a file and have a delegation? */
3924*7c478bd9Sstevel@tonic-gate 	if (fp = rfs4_lookup_and_findfile(ndvp, nnm, &targvp, NULL, cs->cr)) {
3925*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_delegated_byfp(FWRITE, fp, TRUE, TRUE, TRUE,
3926*7c478bd9Sstevel@tonic-gate 						NULL)) {
3927*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_DELAY;
3928*7c478bd9Sstevel@tonic-gate 			goto err_out;
3929*7c478bd9Sstevel@tonic-gate 		}
3930*7c478bd9Sstevel@tonic-gate 	}
3931*7c478bd9Sstevel@tonic-gate 	fp_rele_grant_hold = 1;
3932*7c478bd9Sstevel@tonic-gate 
3933*7c478bd9Sstevel@tonic-gate 
3934*7c478bd9Sstevel@tonic-gate 	/* Check for NBMAND lock on both source and target */
3935*7c478bd9Sstevel@tonic-gate 	if (nbl_need_check(srcvp)) {
3936*7c478bd9Sstevel@tonic-gate 		nbl_start_crit(srcvp, RW_READER);
3937*7c478bd9Sstevel@tonic-gate 		in_crit_src = 1;
3938*7c478bd9Sstevel@tonic-gate 		if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0)) {
3939*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_FILE_OPEN;
3940*7c478bd9Sstevel@tonic-gate 			goto err_out;
3941*7c478bd9Sstevel@tonic-gate 		}
3942*7c478bd9Sstevel@tonic-gate 	}
3943*7c478bd9Sstevel@tonic-gate 
3944*7c478bd9Sstevel@tonic-gate 	if (targvp && nbl_need_check(targvp)) {
3945*7c478bd9Sstevel@tonic-gate 		nbl_start_crit(targvp, RW_READER);
3946*7c478bd9Sstevel@tonic-gate 		in_crit_targ = 1;
3947*7c478bd9Sstevel@tonic-gate 		if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0)) {
3948*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_FILE_OPEN;
3949*7c478bd9Sstevel@tonic-gate 			goto err_out;
3950*7c478bd9Sstevel@tonic-gate 		}
3951*7c478bd9Sstevel@tonic-gate 	}
3952*7c478bd9Sstevel@tonic-gate 
3953*7c478bd9Sstevel@tonic-gate 	/* Get source "before" change value */
3954*7c478bd9Sstevel@tonic-gate 	obdva.va_mask = AT_CTIME|AT_SEQ;
3955*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(odvp, &obdva, 0, cs->cr);
3956*7c478bd9Sstevel@tonic-gate 	if (!error) {
3957*7c478bd9Sstevel@tonic-gate 		nbdva.va_mask = AT_CTIME|AT_SEQ;
3958*7c478bd9Sstevel@tonic-gate 		error = VOP_GETATTR(ndvp, &nbdva, 0, cs->cr);
3959*7c478bd9Sstevel@tonic-gate 	}
3960*7c478bd9Sstevel@tonic-gate 	if (error) {
3961*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
3962*7c478bd9Sstevel@tonic-gate 		goto err_out;
3963*7c478bd9Sstevel@tonic-gate 	}
3964*7c478bd9Sstevel@tonic-gate 
3965*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->source_cinfo.before, obdva.va_ctime)
3966*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->target_cinfo.before, nbdva.va_ctime)
3967*7c478bd9Sstevel@tonic-gate 
3968*7c478bd9Sstevel@tonic-gate 	if ((error = VOP_RENAME(odvp, onm, ndvp, nnm, cs->cr)) == 0 &&
3969*7c478bd9Sstevel@tonic-gate 		fp != NULL) {
3970*7c478bd9Sstevel@tonic-gate 		struct vattr va;
3971*7c478bd9Sstevel@tonic-gate 
3972*7c478bd9Sstevel@tonic-gate 		va.va_mask = AT_NLINK;
3973*7c478bd9Sstevel@tonic-gate 		if (!VOP_GETATTR(fp->vp, &va, 0, cs->cr) &&
3974*7c478bd9Sstevel@tonic-gate 			va.va_nlink == 0) {
3975*7c478bd9Sstevel@tonic-gate 			/* The file is gone and so should the state */
3976*7c478bd9Sstevel@tonic-gate 			if (in_crit_targ) {
3977*7c478bd9Sstevel@tonic-gate 				nbl_end_crit(targvp);
3978*7c478bd9Sstevel@tonic-gate 				in_crit_targ = 0;
3979*7c478bd9Sstevel@tonic-gate 			}
3980*7c478bd9Sstevel@tonic-gate 			rfs4_close_all_state(fp);
3981*7c478bd9Sstevel@tonic-gate 		}
3982*7c478bd9Sstevel@tonic-gate 	}
3983*7c478bd9Sstevel@tonic-gate 
3984*7c478bd9Sstevel@tonic-gate 	if (in_crit_src)
3985*7c478bd9Sstevel@tonic-gate 		nbl_end_crit(srcvp);
3986*7c478bd9Sstevel@tonic-gate 	if (srcvp)
3987*7c478bd9Sstevel@tonic-gate 		VN_RELE(srcvp);
3988*7c478bd9Sstevel@tonic-gate 	if (in_crit_targ)
3989*7c478bd9Sstevel@tonic-gate 		nbl_end_crit(targvp);
3990*7c478bd9Sstevel@tonic-gate 	if (targvp)
3991*7c478bd9Sstevel@tonic-gate 		VN_RELE(targvp);
3992*7c478bd9Sstevel@tonic-gate 
3993*7c478bd9Sstevel@tonic-gate 	if (sfp) {
3994*7c478bd9Sstevel@tonic-gate 		rfs4_clear_dont_grant(sfp);
3995*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(sfp);
3996*7c478bd9Sstevel@tonic-gate 	}
3997*7c478bd9Sstevel@tonic-gate 	if (fp) {
3998*7c478bd9Sstevel@tonic-gate 		rfs4_clear_dont_grant(fp);
3999*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(fp);
4000*7c478bd9Sstevel@tonic-gate 	}
4001*7c478bd9Sstevel@tonic-gate 
4002*7c478bd9Sstevel@tonic-gate 	kmem_free(onm, olen);
4003*7c478bd9Sstevel@tonic-gate 	kmem_free(nnm, nlen);
4004*7c478bd9Sstevel@tonic-gate 
4005*7c478bd9Sstevel@tonic-gate 	/*
4006*7c478bd9Sstevel@tonic-gate 	 * Get the initial "after" sequence number, if it fails, set to zero
4007*7c478bd9Sstevel@tonic-gate 	 */
4008*7c478bd9Sstevel@tonic-gate 	oidva.va_mask = AT_SEQ;
4009*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(odvp, &oidva, 0, cs->cr))
4010*7c478bd9Sstevel@tonic-gate 		oidva.va_seq = 0;
4011*7c478bd9Sstevel@tonic-gate 
4012*7c478bd9Sstevel@tonic-gate 	nidva.va_mask = AT_SEQ;
4013*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(ndvp, &nidva, 0, cs->cr))
4014*7c478bd9Sstevel@tonic-gate 		nidva.va_seq = 0;
4015*7c478bd9Sstevel@tonic-gate 
4016*7c478bd9Sstevel@tonic-gate 	/*
4017*7c478bd9Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
4018*7c478bd9Sstevel@tonic-gate 	 */
4019*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(odvp, 0, cs->cr);
4020*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(ndvp, 0, cs->cr);
4021*7c478bd9Sstevel@tonic-gate 
4022*7c478bd9Sstevel@tonic-gate 	if (error) {
4023*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
4024*7c478bd9Sstevel@tonic-gate 		return;
4025*7c478bd9Sstevel@tonic-gate 	}
4026*7c478bd9Sstevel@tonic-gate 
4027*7c478bd9Sstevel@tonic-gate 	/*
4028*7c478bd9Sstevel@tonic-gate 	 * Get "after" change values, if it fails, simply return the
4029*7c478bd9Sstevel@tonic-gate 	 * before value.
4030*7c478bd9Sstevel@tonic-gate 	 */
4031*7c478bd9Sstevel@tonic-gate 	oadva.va_mask = AT_CTIME|AT_SEQ;
4032*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(odvp, &oadva, 0, cs->cr)) {
4033*7c478bd9Sstevel@tonic-gate 		oadva.va_ctime = obdva.va_ctime;
4034*7c478bd9Sstevel@tonic-gate 		oadva.va_seq = 0;
4035*7c478bd9Sstevel@tonic-gate 	}
4036*7c478bd9Sstevel@tonic-gate 
4037*7c478bd9Sstevel@tonic-gate 	nadva.va_mask = AT_CTIME|AT_SEQ;
4038*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(odvp, &nadva, 0, cs->cr)) {
4039*7c478bd9Sstevel@tonic-gate 		nadva.va_ctime = nbdva.va_ctime;
4040*7c478bd9Sstevel@tonic-gate 		nadva.va_seq = 0;
4041*7c478bd9Sstevel@tonic-gate 	}
4042*7c478bd9Sstevel@tonic-gate 
4043*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->source_cinfo.after, oadva.va_ctime)
4044*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(resp->target_cinfo.after, nadva.va_ctime)
4045*7c478bd9Sstevel@tonic-gate 
4046*7c478bd9Sstevel@tonic-gate 	/*
4047*7c478bd9Sstevel@tonic-gate 	 * The cinfo.atomic = TRUE only if we have
4048*7c478bd9Sstevel@tonic-gate 	 * non-zero va_seq's, and it has incremented by exactly one
4049*7c478bd9Sstevel@tonic-gate 	 * during the VOP_RENAME and it didn't change during the VOP_FSYNC.
4050*7c478bd9Sstevel@tonic-gate 	 */
4051*7c478bd9Sstevel@tonic-gate 	if (obdva.va_seq && oidva.va_seq && oadva.va_seq &&
4052*7c478bd9Sstevel@tonic-gate 			oidva.va_seq == (obdva.va_seq + 1) &&
4053*7c478bd9Sstevel@tonic-gate 			oidva.va_seq == oadva.va_seq)
4054*7c478bd9Sstevel@tonic-gate 		resp->source_cinfo.atomic = TRUE;
4055*7c478bd9Sstevel@tonic-gate 	else
4056*7c478bd9Sstevel@tonic-gate 		resp->source_cinfo.atomic = FALSE;
4057*7c478bd9Sstevel@tonic-gate 
4058*7c478bd9Sstevel@tonic-gate 	if (nbdva.va_seq && nidva.va_seq && nadva.va_seq &&
4059*7c478bd9Sstevel@tonic-gate 			nidva.va_seq == (nbdva.va_seq + 1) &&
4060*7c478bd9Sstevel@tonic-gate 			nidva.va_seq == nadva.va_seq)
4061*7c478bd9Sstevel@tonic-gate 		resp->target_cinfo.atomic = TRUE;
4062*7c478bd9Sstevel@tonic-gate 	else
4063*7c478bd9Sstevel@tonic-gate 		resp->target_cinfo.atomic = FALSE;
4064*7c478bd9Sstevel@tonic-gate 
4065*7c478bd9Sstevel@tonic-gate #ifdef	VOLATILE_FH_TEST
4066*7c478bd9Sstevel@tonic-gate 	{
4067*7c478bd9Sstevel@tonic-gate 	extern void add_volrnm_fh(struct exportinfo *, vnode_t *);
4068*7c478bd9Sstevel@tonic-gate 
4069*7c478bd9Sstevel@tonic-gate 	/*
4070*7c478bd9Sstevel@tonic-gate 	 * Add the renamed file handle to the volatile rename list
4071*7c478bd9Sstevel@tonic-gate 	 */
4072*7c478bd9Sstevel@tonic-gate 	if (cs->exi->exi_export.ex_flags & EX_VOLRNM) {
4073*7c478bd9Sstevel@tonic-gate 		/* file handles may expire on rename */
4074*7c478bd9Sstevel@tonic-gate 		vnode_t *vp;
4075*7c478bd9Sstevel@tonic-gate 
4076*7c478bd9Sstevel@tonic-gate 		nnm = utf8_to_fn(&args->newname, &nlen, NULL);
4077*7c478bd9Sstevel@tonic-gate 		/*
4078*7c478bd9Sstevel@tonic-gate 		 * Already know that nnm will be a valid string
4079*7c478bd9Sstevel@tonic-gate 		 */
4080*7c478bd9Sstevel@tonic-gate 		error = VOP_LOOKUP(ndvp, nnm, &vp, NULL, 0, NULL, cs->cr);
4081*7c478bd9Sstevel@tonic-gate 		kmem_free(nnm, nlen);
4082*7c478bd9Sstevel@tonic-gate 		if (!error) {
4083*7c478bd9Sstevel@tonic-gate 			add_volrnm_fh(cs->exi, vp);
4084*7c478bd9Sstevel@tonic-gate 			VN_RELE(vp);
4085*7c478bd9Sstevel@tonic-gate 		}
4086*7c478bd9Sstevel@tonic-gate 	}
4087*7c478bd9Sstevel@tonic-gate 	}
4088*7c478bd9Sstevel@tonic-gate #endif	/* VOLATILE_FH_TEST */
4089*7c478bd9Sstevel@tonic-gate 
4090*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
4091*7c478bd9Sstevel@tonic-gate 	return;
4092*7c478bd9Sstevel@tonic-gate 
4093*7c478bd9Sstevel@tonic-gate err_out:
4094*7c478bd9Sstevel@tonic-gate 	kmem_free(onm, olen);
4095*7c478bd9Sstevel@tonic-gate 	kmem_free(nnm, nlen);
4096*7c478bd9Sstevel@tonic-gate 
4097*7c478bd9Sstevel@tonic-gate 	if (in_crit_src) nbl_end_crit(srcvp);
4098*7c478bd9Sstevel@tonic-gate 	if (in_crit_targ) nbl_end_crit(targvp);
4099*7c478bd9Sstevel@tonic-gate 	if (targvp) VN_RELE(targvp);
4100*7c478bd9Sstevel@tonic-gate 	if (srcvp) VN_RELE(srcvp);
4101*7c478bd9Sstevel@tonic-gate 	if (sfp) {
4102*7c478bd9Sstevel@tonic-gate 		if (sfp_rele_grant_hold) rfs4_clear_dont_grant(sfp);
4103*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(sfp);
4104*7c478bd9Sstevel@tonic-gate 	}
4105*7c478bd9Sstevel@tonic-gate 	if (fp) {
4106*7c478bd9Sstevel@tonic-gate 		if (fp_rele_grant_hold) rfs4_clear_dont_grant(fp);
4107*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(fp);
4108*7c478bd9Sstevel@tonic-gate 	}
4109*7c478bd9Sstevel@tonic-gate }
4110*7c478bd9Sstevel@tonic-gate 
4111*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4112*7c478bd9Sstevel@tonic-gate static void
4113*7c478bd9Sstevel@tonic-gate rfs4_op_renew(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
4114*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4115*7c478bd9Sstevel@tonic-gate {
4116*7c478bd9Sstevel@tonic-gate 	RENEW4args *args = &argop->nfs_argop4_u.oprenew;
4117*7c478bd9Sstevel@tonic-gate 	RENEW4res *resp = &resop->nfs_resop4_u.oprenew;
4118*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp;
4119*7c478bd9Sstevel@tonic-gate 
4120*7c478bd9Sstevel@tonic-gate 	if ((cp = rfs4_findclient_by_id(args->clientid, FALSE)) == NULL) {
4121*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status =
4122*7c478bd9Sstevel@tonic-gate 			rfs4_check_clientid(&args->clientid, 0);
4123*7c478bd9Sstevel@tonic-gate 		return;
4124*7c478bd9Sstevel@tonic-gate 	}
4125*7c478bd9Sstevel@tonic-gate 
4126*7c478bd9Sstevel@tonic-gate 	if (rfs4_lease_expired(cp)) {
4127*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
4128*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
4129*7c478bd9Sstevel@tonic-gate 		return;
4130*7c478bd9Sstevel@tonic-gate 	}
4131*7c478bd9Sstevel@tonic-gate 
4132*7c478bd9Sstevel@tonic-gate 	rfs4_update_lease(cp);
4133*7c478bd9Sstevel@tonic-gate 
4134*7c478bd9Sstevel@tonic-gate 	mutex_enter(cp->cbinfo.cb_lock);
4135*7c478bd9Sstevel@tonic-gate 	if (cp->cbinfo.cb_notified_of_cb_path_down == FALSE) {
4136*7c478bd9Sstevel@tonic-gate 		cp->cbinfo.cb_notified_of_cb_path_down = TRUE;
4137*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_CB_PATH_DOWN;
4138*7c478bd9Sstevel@tonic-gate 	} else {
4139*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
4140*7c478bd9Sstevel@tonic-gate 	}
4141*7c478bd9Sstevel@tonic-gate 	mutex_exit(cp->cbinfo.cb_lock);
4142*7c478bd9Sstevel@tonic-gate 
4143*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(cp);
4144*7c478bd9Sstevel@tonic-gate 
4145*7c478bd9Sstevel@tonic-gate }
4146*7c478bd9Sstevel@tonic-gate 
4147*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4148*7c478bd9Sstevel@tonic-gate static void
4149*7c478bd9Sstevel@tonic-gate rfs4_op_restorefh(nfs_argop4 *args, nfs_resop4 *resop, struct svc_req *req,
4150*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4151*7c478bd9Sstevel@tonic-gate {
4152*7c478bd9Sstevel@tonic-gate 	RESTOREFH4res *resp = &resop->nfs_resop4_u.oprestorefh;
4153*7c478bd9Sstevel@tonic-gate 
4154*7c478bd9Sstevel@tonic-gate 	/* No need to check cs->access - we are not accessing any object */
4155*7c478bd9Sstevel@tonic-gate 	if ((cs->saved_vp == NULL) || (cs->saved_fh.nfs_fh4_val == NULL)) {
4156*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_RESTOREFH;
4157*7c478bd9Sstevel@tonic-gate 		return;
4158*7c478bd9Sstevel@tonic-gate 	}
4159*7c478bd9Sstevel@tonic-gate 	if (cs->vp != NULL) {
4160*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
4161*7c478bd9Sstevel@tonic-gate 	}
4162*7c478bd9Sstevel@tonic-gate 	cs->vp = cs->saved_vp;
4163*7c478bd9Sstevel@tonic-gate 	cs->saved_vp = NULL;
4164*7c478bd9Sstevel@tonic-gate 	cs->exi = cs->saved_exi;
4165*7c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(&cs->saved_fh, &cs->fh);
4166*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
4167*7c478bd9Sstevel@tonic-gate 	cs->deleg = FALSE;
4168*7c478bd9Sstevel@tonic-gate }
4169*7c478bd9Sstevel@tonic-gate 
4170*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4171*7c478bd9Sstevel@tonic-gate static void
4172*7c478bd9Sstevel@tonic-gate rfs4_op_savefh(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
4173*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4174*7c478bd9Sstevel@tonic-gate {
4175*7c478bd9Sstevel@tonic-gate 	SAVEFH4res *resp = &resop->nfs_resop4_u.opsavefh;
4176*7c478bd9Sstevel@tonic-gate 
4177*7c478bd9Sstevel@tonic-gate 	/* No need to check cs->access - we are not accessing any object */
4178*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
4179*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
4180*7c478bd9Sstevel@tonic-gate 		return;
4181*7c478bd9Sstevel@tonic-gate 	}
4182*7c478bd9Sstevel@tonic-gate 	if (cs->saved_vp != NULL) {
4183*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->saved_vp);
4184*7c478bd9Sstevel@tonic-gate 	}
4185*7c478bd9Sstevel@tonic-gate 	cs->saved_vp = cs->vp;
4186*7c478bd9Sstevel@tonic-gate 	VN_HOLD(cs->saved_vp);
4187*7c478bd9Sstevel@tonic-gate 	cs->saved_exi = cs->exi;
4188*7c478bd9Sstevel@tonic-gate 	/*
4189*7c478bd9Sstevel@tonic-gate 	 * since SAVEFH is fairly rare, don't alloc space for its fh
4190*7c478bd9Sstevel@tonic-gate 	 * unless necessary.
4191*7c478bd9Sstevel@tonic-gate 	 */
4192*7c478bd9Sstevel@tonic-gate 	if (cs->saved_fh.nfs_fh4_val == NULL) {
4193*7c478bd9Sstevel@tonic-gate 		cs->saved_fh.nfs_fh4_val = kmem_alloc(NFS4_FHSIZE, KM_SLEEP);
4194*7c478bd9Sstevel@tonic-gate 	}
4195*7c478bd9Sstevel@tonic-gate 	nfs_fh4_copy(&cs->fh, &cs->saved_fh);
4196*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
4197*7c478bd9Sstevel@tonic-gate }
4198*7c478bd9Sstevel@tonic-gate 
4199*7c478bd9Sstevel@tonic-gate /*
4200*7c478bd9Sstevel@tonic-gate  * rfs4_verify_attr is called when nfsv4 Setattr failed, but we wish to
4201*7c478bd9Sstevel@tonic-gate  * return the bitmap of attrs that were set successfully. It is also
4202*7c478bd9Sstevel@tonic-gate  * called by Verify/Nverify to test the vattr/vfsstat attrs. It should
4203*7c478bd9Sstevel@tonic-gate  * always be called only after rfs4_do_set_attrs().
4204*7c478bd9Sstevel@tonic-gate  *
4205*7c478bd9Sstevel@tonic-gate  * Verify that the attributes are same as the expected ones. sargp->vap
4206*7c478bd9Sstevel@tonic-gate  * and sargp->sbp contain the input attributes as translated from fattr4.
4207*7c478bd9Sstevel@tonic-gate  *
4208*7c478bd9Sstevel@tonic-gate  * This function verifies only the attrs that correspond to a vattr or
4209*7c478bd9Sstevel@tonic-gate  * vfsstat struct. That is because of the extra step needed to get the
4210*7c478bd9Sstevel@tonic-gate  * corresponding system structs. Other attributes have already been set or
4211*7c478bd9Sstevel@tonic-gate  * verified by do_rfs4_set_attrs.
4212*7c478bd9Sstevel@tonic-gate  *
4213*7c478bd9Sstevel@tonic-gate  * Return 0 if all attrs match, -1 if some don't, error if error processing.
4214*7c478bd9Sstevel@tonic-gate  */
4215*7c478bd9Sstevel@tonic-gate static int
4216*7c478bd9Sstevel@tonic-gate rfs4_verify_attr(struct nfs4_svgetit_arg *sargp,
4217*7c478bd9Sstevel@tonic-gate 	bitmap4 *resp, struct nfs4_ntov_table *ntovp)
4218*7c478bd9Sstevel@tonic-gate {
4219*7c478bd9Sstevel@tonic-gate 	int error, ret_error = 0;
4220*7c478bd9Sstevel@tonic-gate 	int i, k;
4221*7c478bd9Sstevel@tonic-gate 	uint_t sva_mask = sargp->vap->va_mask;
4222*7c478bd9Sstevel@tonic-gate 	uint_t vbit;
4223*7c478bd9Sstevel@tonic-gate 	union nfs4_attr_u *na;
4224*7c478bd9Sstevel@tonic-gate 	uint8_t *amap;
4225*7c478bd9Sstevel@tonic-gate 	bool_t getsb = ntovp->vfsstat;
4226*7c478bd9Sstevel@tonic-gate 
4227*7c478bd9Sstevel@tonic-gate 	if (sva_mask != 0) {
4228*7c478bd9Sstevel@tonic-gate 		/*
4229*7c478bd9Sstevel@tonic-gate 		 * Okay to overwrite sargp->vap because we verify based
4230*7c478bd9Sstevel@tonic-gate 		 * on the incoming values.
4231*7c478bd9Sstevel@tonic-gate 		 */
4232*7c478bd9Sstevel@tonic-gate 		ret_error = VOP_GETATTR(sargp->cs->vp, sargp->vap, 0,
4233*7c478bd9Sstevel@tonic-gate 				sargp->cs->cr);
4234*7c478bd9Sstevel@tonic-gate 		if (ret_error) {
4235*7c478bd9Sstevel@tonic-gate 			if (resp == NULL)
4236*7c478bd9Sstevel@tonic-gate 				return (ret_error);
4237*7c478bd9Sstevel@tonic-gate 			/*
4238*7c478bd9Sstevel@tonic-gate 			 * Must return bitmap of successful attrs
4239*7c478bd9Sstevel@tonic-gate 			 */
4240*7c478bd9Sstevel@tonic-gate 			sva_mask = 0;	/* to prevent checking vap later */
4241*7c478bd9Sstevel@tonic-gate 		} else {
4242*7c478bd9Sstevel@tonic-gate 			/*
4243*7c478bd9Sstevel@tonic-gate 			 * Some file systems clobber va_mask. it is probably
4244*7c478bd9Sstevel@tonic-gate 			 * wrong of them to do so, nonethless we practice
4245*7c478bd9Sstevel@tonic-gate 			 * defensive coding.
4246*7c478bd9Sstevel@tonic-gate 			 * See bug id 4276830.
4247*7c478bd9Sstevel@tonic-gate 			 */
4248*7c478bd9Sstevel@tonic-gate 			sargp->vap->va_mask = sva_mask;
4249*7c478bd9Sstevel@tonic-gate 		}
4250*7c478bd9Sstevel@tonic-gate 	}
4251*7c478bd9Sstevel@tonic-gate 
4252*7c478bd9Sstevel@tonic-gate 	if (getsb) {
4253*7c478bd9Sstevel@tonic-gate 		/*
4254*7c478bd9Sstevel@tonic-gate 		 * Now get the superblock and loop on the bitmap, as there is
4255*7c478bd9Sstevel@tonic-gate 		 * no simple way of translating from superblock to bitmap4.
4256*7c478bd9Sstevel@tonic-gate 		 */
4257*7c478bd9Sstevel@tonic-gate 		ret_error = VFS_STATVFS(sargp->cs->vp->v_vfsp, sargp->sbp);
4258*7c478bd9Sstevel@tonic-gate 		if (ret_error) {
4259*7c478bd9Sstevel@tonic-gate 			if (resp == NULL)
4260*7c478bd9Sstevel@tonic-gate 				goto errout;
4261*7c478bd9Sstevel@tonic-gate 			getsb = FALSE;
4262*7c478bd9Sstevel@tonic-gate 		}
4263*7c478bd9Sstevel@tonic-gate 	}
4264*7c478bd9Sstevel@tonic-gate 
4265*7c478bd9Sstevel@tonic-gate 	/*
4266*7c478bd9Sstevel@tonic-gate 	 * Now loop and verify each attribute which getattr returned
4267*7c478bd9Sstevel@tonic-gate 	 * whether it's the same as the input.
4268*7c478bd9Sstevel@tonic-gate 	 */
4269*7c478bd9Sstevel@tonic-gate 	if (resp == NULL && !getsb && (sva_mask == 0))
4270*7c478bd9Sstevel@tonic-gate 		goto errout;
4271*7c478bd9Sstevel@tonic-gate 
4272*7c478bd9Sstevel@tonic-gate 	na = ntovp->na;
4273*7c478bd9Sstevel@tonic-gate 	amap = ntovp->amap;
4274*7c478bd9Sstevel@tonic-gate 	k = 0;
4275*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < ntovp->attrcnt; i++, na++, amap++) {
4276*7c478bd9Sstevel@tonic-gate 		k = *amap;
4277*7c478bd9Sstevel@tonic-gate 		ASSERT(nfs4_ntov_map[k].nval == k);
4278*7c478bd9Sstevel@tonic-gate 		vbit = nfs4_ntov_map[k].vbit;
4279*7c478bd9Sstevel@tonic-gate 
4280*7c478bd9Sstevel@tonic-gate 		/*
4281*7c478bd9Sstevel@tonic-gate 		 * If vattr attribute but VOP_GETATTR failed, or it's
4282*7c478bd9Sstevel@tonic-gate 		 * superblock attribute but VFS_STATVFS failed, skip
4283*7c478bd9Sstevel@tonic-gate 		 */
4284*7c478bd9Sstevel@tonic-gate 		if (vbit) {
4285*7c478bd9Sstevel@tonic-gate 			if ((vbit & sva_mask) == 0)
4286*7c478bd9Sstevel@tonic-gate 				continue;
4287*7c478bd9Sstevel@tonic-gate 		} else if (!(getsb && nfs4_ntov_map[k].vfsstat)) {
4288*7c478bd9Sstevel@tonic-gate 			continue;
4289*7c478bd9Sstevel@tonic-gate 		}
4290*7c478bd9Sstevel@tonic-gate 		error = (*nfs4_ntov_map[k].sv_getit)(
4291*7c478bd9Sstevel@tonic-gate 				NFS4ATTR_VERIT, sargp, na);
4292*7c478bd9Sstevel@tonic-gate 		if (resp != NULL) {
4293*7c478bd9Sstevel@tonic-gate 			if (error)
4294*7c478bd9Sstevel@tonic-gate 				ret_error = -1;	/* not all match */
4295*7c478bd9Sstevel@tonic-gate 			else	/* update response bitmap */
4296*7c478bd9Sstevel@tonic-gate 				*resp |= nfs4_ntov_map[k].fbit;
4297*7c478bd9Sstevel@tonic-gate 			continue;
4298*7c478bd9Sstevel@tonic-gate 		}
4299*7c478bd9Sstevel@tonic-gate 		if (error) {
4300*7c478bd9Sstevel@tonic-gate 			ret_error = -1;	/* not all match */
4301*7c478bd9Sstevel@tonic-gate 			break;
4302*7c478bd9Sstevel@tonic-gate 		}
4303*7c478bd9Sstevel@tonic-gate 	}
4304*7c478bd9Sstevel@tonic-gate errout:
4305*7c478bd9Sstevel@tonic-gate 	return (ret_error);
4306*7c478bd9Sstevel@tonic-gate }
4307*7c478bd9Sstevel@tonic-gate 
4308*7c478bd9Sstevel@tonic-gate /*
4309*7c478bd9Sstevel@tonic-gate  * Decode the attribute to be set/verified. If the attr requires a sys op
4310*7c478bd9Sstevel@tonic-gate  * (VOP_GETATTR, VFS_VFSSTAT), and the request is to verify, then don't
4311*7c478bd9Sstevel@tonic-gate  * call the sv_getit function for it, because the sys op hasn't yet been done.
4312*7c478bd9Sstevel@tonic-gate  * Return 0 for success, error code if failed.
4313*7c478bd9Sstevel@tonic-gate  *
4314*7c478bd9Sstevel@tonic-gate  * Note: the decoded arg is not freed here but in nfs4_ntov_table_free.
4315*7c478bd9Sstevel@tonic-gate  */
4316*7c478bd9Sstevel@tonic-gate static int
4317*7c478bd9Sstevel@tonic-gate decode_fattr4_attr(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sargp,
4318*7c478bd9Sstevel@tonic-gate 	int k, XDR *xdrp, bitmap4 *resp_bval, union nfs4_attr_u *nap)
4319*7c478bd9Sstevel@tonic-gate {
4320*7c478bd9Sstevel@tonic-gate 	int error = 0;
4321*7c478bd9Sstevel@tonic-gate 	bool_t set_later;
4322*7c478bd9Sstevel@tonic-gate 
4323*7c478bd9Sstevel@tonic-gate 	sargp->vap->va_mask |= nfs4_ntov_map[k].vbit;
4324*7c478bd9Sstevel@tonic-gate 
4325*7c478bd9Sstevel@tonic-gate 	if ((*nfs4_ntov_map[k].xfunc)(xdrp, nap)) {
4326*7c478bd9Sstevel@tonic-gate 		set_later = nfs4_ntov_map[k].vbit || nfs4_ntov_map[k].vfsstat;
4327*7c478bd9Sstevel@tonic-gate 		/*
4328*7c478bd9Sstevel@tonic-gate 		 * don't verify yet if a vattr or sb dependent attr,
4329*7c478bd9Sstevel@tonic-gate 		 * because we don't have their sys values yet.
4330*7c478bd9Sstevel@tonic-gate 		 * Will be done later.
4331*7c478bd9Sstevel@tonic-gate 		 */
4332*7c478bd9Sstevel@tonic-gate 		if (! (set_later && (cmd == NFS4ATTR_VERIT))) {
4333*7c478bd9Sstevel@tonic-gate 			/*
4334*7c478bd9Sstevel@tonic-gate 			 * ACLs are a special case, since setting the MODE
4335*7c478bd9Sstevel@tonic-gate 			 * conflicts with setting the ACL.  We delay setting
4336*7c478bd9Sstevel@tonic-gate 			 * the ACL until all other attributes have been set.
4337*7c478bd9Sstevel@tonic-gate 			 * The ACL gets set in do_rfs4_op_setattr().
4338*7c478bd9Sstevel@tonic-gate 			 */
4339*7c478bd9Sstevel@tonic-gate 			if (nfs4_ntov_map[k].fbit != FATTR4_ACL_MASK) {
4340*7c478bd9Sstevel@tonic-gate 				error = (*nfs4_ntov_map[k].sv_getit)(cmd,
4341*7c478bd9Sstevel@tonic-gate 				    sargp, nap);
4342*7c478bd9Sstevel@tonic-gate 				if (error) {
4343*7c478bd9Sstevel@tonic-gate 					xdr_free(nfs4_ntov_map[k].xfunc,
4344*7c478bd9Sstevel@tonic-gate 					    (caddr_t)nap);
4345*7c478bd9Sstevel@tonic-gate 				}
4346*7c478bd9Sstevel@tonic-gate 			}
4347*7c478bd9Sstevel@tonic-gate 		}
4348*7c478bd9Sstevel@tonic-gate 	} else {
4349*7c478bd9Sstevel@tonic-gate #ifdef  DEBUG
4350*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "decode_fattr4_attr: error "
4351*7c478bd9Sstevel@tonic-gate 			"decoding attribute %d\n", k);
4352*7c478bd9Sstevel@tonic-gate #endif
4353*7c478bd9Sstevel@tonic-gate 		error = EINVAL;
4354*7c478bd9Sstevel@tonic-gate 	}
4355*7c478bd9Sstevel@tonic-gate 	if (!error && resp_bval && !set_later) {
4356*7c478bd9Sstevel@tonic-gate 		*resp_bval |= nfs4_ntov_map[k].fbit;
4357*7c478bd9Sstevel@tonic-gate 	}
4358*7c478bd9Sstevel@tonic-gate 
4359*7c478bd9Sstevel@tonic-gate 	return (error);
4360*7c478bd9Sstevel@tonic-gate }
4361*7c478bd9Sstevel@tonic-gate 
4362*7c478bd9Sstevel@tonic-gate /*
4363*7c478bd9Sstevel@tonic-gate  * Set vattr based on incoming fattr4 attrs - used by setattr.
4364*7c478bd9Sstevel@tonic-gate  * Set response mask. Ignore any values that are not writable vattr attrs.
4365*7c478bd9Sstevel@tonic-gate  */
4366*7c478bd9Sstevel@tonic-gate static nfsstat4
4367*7c478bd9Sstevel@tonic-gate do_rfs4_set_attrs(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs,
4368*7c478bd9Sstevel@tonic-gate 		struct nfs4_svgetit_arg *sargp, struct nfs4_ntov_table *ntovp,
4369*7c478bd9Sstevel@tonic-gate 		nfs4_attr_cmd_t cmd)
4370*7c478bd9Sstevel@tonic-gate {
4371*7c478bd9Sstevel@tonic-gate 	int error = 0;
4372*7c478bd9Sstevel@tonic-gate 	int i;
4373*7c478bd9Sstevel@tonic-gate 	char *attrs = fattrp->attrlist4;
4374*7c478bd9Sstevel@tonic-gate 	uint32_t attrslen = fattrp->attrlist4_len;
4375*7c478bd9Sstevel@tonic-gate 	XDR xdr;
4376*7c478bd9Sstevel@tonic-gate 	nfsstat4 status = NFS4_OK;
4377*7c478bd9Sstevel@tonic-gate 	vnode_t *vp = cs->vp;
4378*7c478bd9Sstevel@tonic-gate 	union nfs4_attr_u *na;
4379*7c478bd9Sstevel@tonic-gate 	uint8_t *amap;
4380*7c478bd9Sstevel@tonic-gate 
4381*7c478bd9Sstevel@tonic-gate #ifndef lint
4382*7c478bd9Sstevel@tonic-gate 	/*
4383*7c478bd9Sstevel@tonic-gate 	 * Make sure that maximum attribute number can be expressed as an
4384*7c478bd9Sstevel@tonic-gate 	 * 8 bit quantity.
4385*7c478bd9Sstevel@tonic-gate 	 */
4386*7c478bd9Sstevel@tonic-gate 	ASSERT(NFS4_MAXNUM_ATTRS <= (UINT8_MAX + 1));
4387*7c478bd9Sstevel@tonic-gate #endif
4388*7c478bd9Sstevel@tonic-gate 
4389*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
4390*7c478bd9Sstevel@tonic-gate 		if (resp)
4391*7c478bd9Sstevel@tonic-gate 			*resp = 0;
4392*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOFILEHANDLE);
4393*7c478bd9Sstevel@tonic-gate 	}
4394*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
4395*7c478bd9Sstevel@tonic-gate 		if (resp)
4396*7c478bd9Sstevel@tonic-gate 			*resp = 0;
4397*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ACCESS);
4398*7c478bd9Sstevel@tonic-gate 	}
4399*7c478bd9Sstevel@tonic-gate 
4400*7c478bd9Sstevel@tonic-gate 	sargp->op = cmd;
4401*7c478bd9Sstevel@tonic-gate 	sargp->cs = cs;
4402*7c478bd9Sstevel@tonic-gate 	sargp->flag = 0;	/* may be set later */
4403*7c478bd9Sstevel@tonic-gate 	sargp->vap->va_mask = 0;
4404*7c478bd9Sstevel@tonic-gate 	sargp->rdattr_error = NFS4_OK;
4405*7c478bd9Sstevel@tonic-gate 	sargp->rdattr_error_req = FALSE;
4406*7c478bd9Sstevel@tonic-gate 	/* sargp->sbp is set by the caller */
4407*7c478bd9Sstevel@tonic-gate 
4408*7c478bd9Sstevel@tonic-gate 	xdrmem_create(&xdr, attrs, attrslen, XDR_DECODE);
4409*7c478bd9Sstevel@tonic-gate 
4410*7c478bd9Sstevel@tonic-gate 	na = ntovp->na;
4411*7c478bd9Sstevel@tonic-gate 	amap = ntovp->amap;
4412*7c478bd9Sstevel@tonic-gate 
4413*7c478bd9Sstevel@tonic-gate 	/*
4414*7c478bd9Sstevel@tonic-gate 	 * The following loop iterates on the nfs4_ntov_map checking
4415*7c478bd9Sstevel@tonic-gate 	 * if the fbit is set in the requested bitmap.
4416*7c478bd9Sstevel@tonic-gate 	 * If set then we process the arguments using the
4417*7c478bd9Sstevel@tonic-gate 	 * rfs4_fattr4 conversion functions to populate the setattr
4418*7c478bd9Sstevel@tonic-gate 	 * vattr and va_mask. Any settable attrs that are not using vattr
4419*7c478bd9Sstevel@tonic-gate 	 * will be set in this loop.
4420*7c478bd9Sstevel@tonic-gate 	 */
4421*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nfs4_ntov_map_size; i++) {
4422*7c478bd9Sstevel@tonic-gate 		if (!(fattrp->attrmask & nfs4_ntov_map[i].fbit)) {
4423*7c478bd9Sstevel@tonic-gate 			continue;
4424*7c478bd9Sstevel@tonic-gate 		}
4425*7c478bd9Sstevel@tonic-gate 		/*
4426*7c478bd9Sstevel@tonic-gate 		 * If setattr, must be a writable attr.
4427*7c478bd9Sstevel@tonic-gate 		 * If verify/nverify, must be a readable attr.
4428*7c478bd9Sstevel@tonic-gate 		 */
4429*7c478bd9Sstevel@tonic-gate 		if ((error = (*nfs4_ntov_map[i].sv_getit)(
4430*7c478bd9Sstevel@tonic-gate 				    NFS4ATTR_SUPPORTED, sargp, NULL)) != 0) {
4431*7c478bd9Sstevel@tonic-gate 			/*
4432*7c478bd9Sstevel@tonic-gate 			 * Client tries to set/verify an
4433*7c478bd9Sstevel@tonic-gate 			 * unsupported attribute, tries to set
4434*7c478bd9Sstevel@tonic-gate 			 * a read only attr or verify a write
4435*7c478bd9Sstevel@tonic-gate 			 * only one - error!
4436*7c478bd9Sstevel@tonic-gate 			 */
4437*7c478bd9Sstevel@tonic-gate 			break;
4438*7c478bd9Sstevel@tonic-gate 		}
4439*7c478bd9Sstevel@tonic-gate 		/*
4440*7c478bd9Sstevel@tonic-gate 		 * Decode the attribute to set/verify
4441*7c478bd9Sstevel@tonic-gate 		 */
4442*7c478bd9Sstevel@tonic-gate 		error = decode_fattr4_attr(cmd, sargp, nfs4_ntov_map[i].nval,
4443*7c478bd9Sstevel@tonic-gate 					&xdr, resp ? resp : NULL, na);
4444*7c478bd9Sstevel@tonic-gate 		if (error)
4445*7c478bd9Sstevel@tonic-gate 			break;
4446*7c478bd9Sstevel@tonic-gate 		*amap++ = (uint8_t)nfs4_ntov_map[i].nval;
4447*7c478bd9Sstevel@tonic-gate 		na++;
4448*7c478bd9Sstevel@tonic-gate 		(ntovp->attrcnt)++;
4449*7c478bd9Sstevel@tonic-gate 		if (nfs4_ntov_map[i].vfsstat)
4450*7c478bd9Sstevel@tonic-gate 			ntovp->vfsstat = TRUE;
4451*7c478bd9Sstevel@tonic-gate 	}
4452*7c478bd9Sstevel@tonic-gate 
4453*7c478bd9Sstevel@tonic-gate 	if (error != 0)
4454*7c478bd9Sstevel@tonic-gate 		status = (error == ENOTSUP ?	NFS4ERR_ATTRNOTSUPP :
4455*7c478bd9Sstevel@tonic-gate 						puterrno4(error));
4456*7c478bd9Sstevel@tonic-gate 	/* xdrmem_destroy(&xdrs); */	/* NO-OP */
4457*7c478bd9Sstevel@tonic-gate 	return (status);
4458*7c478bd9Sstevel@tonic-gate }
4459*7c478bd9Sstevel@tonic-gate 
4460*7c478bd9Sstevel@tonic-gate static nfsstat4
4461*7c478bd9Sstevel@tonic-gate do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs,
4462*7c478bd9Sstevel@tonic-gate 		stateid4 *stateid)
4463*7c478bd9Sstevel@tonic-gate {
4464*7c478bd9Sstevel@tonic-gate 	int error = 0;
4465*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg sarg;
4466*7c478bd9Sstevel@tonic-gate 	bool_t trunc;
4467*7c478bd9Sstevel@tonic-gate 
4468*7c478bd9Sstevel@tonic-gate 	nfsstat4 status = NFS4_OK;
4469*7c478bd9Sstevel@tonic-gate 	cred_t *cr = cs->cr;
4470*7c478bd9Sstevel@tonic-gate 	vnode_t *vp = cs->vp;
4471*7c478bd9Sstevel@tonic-gate 	struct nfs4_ntov_table ntov;
4472*7c478bd9Sstevel@tonic-gate 	struct statvfs64 sb;
4473*7c478bd9Sstevel@tonic-gate 	struct vattr bva;
4474*7c478bd9Sstevel@tonic-gate 	struct flock64 bf;
4475*7c478bd9Sstevel@tonic-gate 	int in_crit = 0;
4476*7c478bd9Sstevel@tonic-gate 	uint_t saved_mask = 0;
4477*7c478bd9Sstevel@tonic-gate 	caller_context_t ct;
4478*7c478bd9Sstevel@tonic-gate 
4479*7c478bd9Sstevel@tonic-gate 	*resp = 0;
4480*7c478bd9Sstevel@tonic-gate 	sarg.sbp = &sb;
4481*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_init(&ntov);
4482*7c478bd9Sstevel@tonic-gate 	status = do_rfs4_set_attrs(resp, fattrp, cs, &sarg, &ntov,
4483*7c478bd9Sstevel@tonic-gate 			NFS4ATTR_SETIT);
4484*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
4485*7c478bd9Sstevel@tonic-gate 		/*
4486*7c478bd9Sstevel@tonic-gate 		 * failed set attrs
4487*7c478bd9Sstevel@tonic-gate 		 */
4488*7c478bd9Sstevel@tonic-gate 		goto done;
4489*7c478bd9Sstevel@tonic-gate 	}
4490*7c478bd9Sstevel@tonic-gate 	if ((sarg.vap->va_mask == 0) &&
4491*7c478bd9Sstevel@tonic-gate 	    (! (fattrp->attrmask & FATTR4_ACL_MASK))) {
4492*7c478bd9Sstevel@tonic-gate 		/*
4493*7c478bd9Sstevel@tonic-gate 		 * no further work to be done
4494*7c478bd9Sstevel@tonic-gate 		 */
4495*7c478bd9Sstevel@tonic-gate 		goto done;
4496*7c478bd9Sstevel@tonic-gate 	}
4497*7c478bd9Sstevel@tonic-gate 
4498*7c478bd9Sstevel@tonic-gate 	/*
4499*7c478bd9Sstevel@tonic-gate 	 * If we got a request to set the ACL and the MODE, only
4500*7c478bd9Sstevel@tonic-gate 	 * allow changing VSUID, VSGID, and VSVTX.  Attempting
4501*7c478bd9Sstevel@tonic-gate 	 * to change any other bits, along with setting an ACL,
4502*7c478bd9Sstevel@tonic-gate 	 * gives NFS4ERR_INVAL.
4503*7c478bd9Sstevel@tonic-gate 	 */
4504*7c478bd9Sstevel@tonic-gate 	if ((fattrp->attrmask & FATTR4_ACL_MASK) &&
4505*7c478bd9Sstevel@tonic-gate 	    (fattrp->attrmask & FATTR4_MODE_MASK)) {
4506*7c478bd9Sstevel@tonic-gate 		vattr_t va;
4507*7c478bd9Sstevel@tonic-gate 
4508*7c478bd9Sstevel@tonic-gate 		va.va_mask = AT_MODE;
4509*7c478bd9Sstevel@tonic-gate 		error = VOP_GETATTR(vp, &va, 0, cs->cr);
4510*7c478bd9Sstevel@tonic-gate 		if (error) {
4511*7c478bd9Sstevel@tonic-gate 			status = puterrno4(error);
4512*7c478bd9Sstevel@tonic-gate 			goto done;
4513*7c478bd9Sstevel@tonic-gate 		}
4514*7c478bd9Sstevel@tonic-gate 		if ((sarg.vap->va_mode ^ va.va_mode) &
4515*7c478bd9Sstevel@tonic-gate 		    ~(VSUID | VSGID | VSVTX)) {
4516*7c478bd9Sstevel@tonic-gate 			status = NFS4ERR_INVAL;
4517*7c478bd9Sstevel@tonic-gate 			goto done;
4518*7c478bd9Sstevel@tonic-gate 		}
4519*7c478bd9Sstevel@tonic-gate 	}
4520*7c478bd9Sstevel@tonic-gate 
4521*7c478bd9Sstevel@tonic-gate 
4522*7c478bd9Sstevel@tonic-gate 	/* Check stateid only if size has been set */
4523*7c478bd9Sstevel@tonic-gate 	if (sarg.vap->va_mask & AT_SIZE) {
4524*7c478bd9Sstevel@tonic-gate 		trunc = (sarg.vap->va_size == 0);
4525*7c478bd9Sstevel@tonic-gate 		status = rfs4_check_stateid(FWRITE, cs->vp, stateid,
4526*7c478bd9Sstevel@tonic-gate 			trunc, &cs->deleg, sarg.vap->va_mask & AT_SIZE);
4527*7c478bd9Sstevel@tonic-gate 		if (status != NFS4_OK)
4528*7c478bd9Sstevel@tonic-gate 			goto done;
4529*7c478bd9Sstevel@tonic-gate 	}
4530*7c478bd9Sstevel@tonic-gate 
4531*7c478bd9Sstevel@tonic-gate 	ct.cc_sysid = 0;
4532*7c478bd9Sstevel@tonic-gate 	ct.cc_pid = 0;
4533*7c478bd9Sstevel@tonic-gate 	ct.cc_caller_id = nfs4_srv_caller_id;
4534*7c478bd9Sstevel@tonic-gate 
4535*7c478bd9Sstevel@tonic-gate 	/* XXX start of possible race with delegations */
4536*7c478bd9Sstevel@tonic-gate 
4537*7c478bd9Sstevel@tonic-gate 	/*
4538*7c478bd9Sstevel@tonic-gate 	 * We need to specially handle size changes because it is
4539*7c478bd9Sstevel@tonic-gate 	 * possible for the client to create a file with read-only
4540*7c478bd9Sstevel@tonic-gate 	 * modes, but with the file opened for writing. If the client
4541*7c478bd9Sstevel@tonic-gate 	 * then tries to set the file size, e.g. ftruncate(3C),
4542*7c478bd9Sstevel@tonic-gate 	 * fcntl(F_FREESP), the normal access checking done in
4543*7c478bd9Sstevel@tonic-gate 	 * VOP_SETATTR would prevent the client from doing it even though
4544*7c478bd9Sstevel@tonic-gate 	 * it should be allowed to do so.  To get around this, we do the
4545*7c478bd9Sstevel@tonic-gate 	 * access checking for ourselves and use VOP_SPACE which doesn't
4546*7c478bd9Sstevel@tonic-gate 	 * do the access checking.
4547*7c478bd9Sstevel@tonic-gate 	 * Also the client should not be allowed to change the file
4548*7c478bd9Sstevel@tonic-gate 	 * size if there is a conflicting non-blocking mandatory lock in
4549*7c478bd9Sstevel@tonic-gate 	 * the region of the change.
4550*7c478bd9Sstevel@tonic-gate 	 */
4551*7c478bd9Sstevel@tonic-gate 	if (vp->v_type == VREG && (sarg.vap->va_mask & AT_SIZE)) {
4552*7c478bd9Sstevel@tonic-gate 		u_offset_t offset;
4553*7c478bd9Sstevel@tonic-gate 		ssize_t length;
4554*7c478bd9Sstevel@tonic-gate 
4555*7c478bd9Sstevel@tonic-gate 		/*
4556*7c478bd9Sstevel@tonic-gate 		 * Check any possible conflict due to NBMAND locks.
4557*7c478bd9Sstevel@tonic-gate 		 * Get into critical region before VOP_GETATTR, so the
4558*7c478bd9Sstevel@tonic-gate 		 * size attribute is valid when checking conflicts.
4559*7c478bd9Sstevel@tonic-gate 		 */
4560*7c478bd9Sstevel@tonic-gate 		if (nbl_need_check(vp)) {
4561*7c478bd9Sstevel@tonic-gate 			nbl_start_crit(vp, RW_READER);
4562*7c478bd9Sstevel@tonic-gate 			in_crit = 1;
4563*7c478bd9Sstevel@tonic-gate 		}
4564*7c478bd9Sstevel@tonic-gate 
4565*7c478bd9Sstevel@tonic-gate 		bva.va_mask = AT_UID|AT_SIZE;
4566*7c478bd9Sstevel@tonic-gate 		if (error = VOP_GETATTR(vp, &bva, 0, cr)) {
4567*7c478bd9Sstevel@tonic-gate 			status = puterrno4(error);
4568*7c478bd9Sstevel@tonic-gate 			goto done;
4569*7c478bd9Sstevel@tonic-gate 		}
4570*7c478bd9Sstevel@tonic-gate 
4571*7c478bd9Sstevel@tonic-gate 		if (in_crit) {
4572*7c478bd9Sstevel@tonic-gate 			if (sarg.vap->va_size < bva.va_size) {
4573*7c478bd9Sstevel@tonic-gate 				offset = sarg.vap->va_size;
4574*7c478bd9Sstevel@tonic-gate 				length = bva.va_size - sarg.vap->va_size;
4575*7c478bd9Sstevel@tonic-gate 			} else {
4576*7c478bd9Sstevel@tonic-gate 				offset = bva.va_size;
4577*7c478bd9Sstevel@tonic-gate 				length = sarg.vap->va_size - bva.va_size;
4578*7c478bd9Sstevel@tonic-gate 			}
4579*7c478bd9Sstevel@tonic-gate 			if (nbl_conflict(vp, NBL_WRITE, offset, length, 0)) {
4580*7c478bd9Sstevel@tonic-gate 				status = NFS4ERR_LOCKED;
4581*7c478bd9Sstevel@tonic-gate 				goto done;
4582*7c478bd9Sstevel@tonic-gate 			}
4583*7c478bd9Sstevel@tonic-gate 		}
4584*7c478bd9Sstevel@tonic-gate 
4585*7c478bd9Sstevel@tonic-gate 		if (crgetuid(cr) == bva.va_uid) {
4586*7c478bd9Sstevel@tonic-gate 			saved_mask = sarg.vap->va_mask;
4587*7c478bd9Sstevel@tonic-gate 			sarg.vap->va_mask &= ~AT_SIZE;
4588*7c478bd9Sstevel@tonic-gate 			bf.l_type = F_WRLCK;
4589*7c478bd9Sstevel@tonic-gate 			bf.l_whence = 0;
4590*7c478bd9Sstevel@tonic-gate 			bf.l_start = (off64_t)sarg.vap->va_size;
4591*7c478bd9Sstevel@tonic-gate 			bf.l_len = 0;
4592*7c478bd9Sstevel@tonic-gate 			bf.l_sysid = 0;
4593*7c478bd9Sstevel@tonic-gate 			bf.l_pid = 0;
4594*7c478bd9Sstevel@tonic-gate 			error = VOP_SPACE(vp, F_FREESP, &bf, FWRITE,
4595*7c478bd9Sstevel@tonic-gate 					(offset_t)sarg.vap->va_size, cr, &ct);
4596*7c478bd9Sstevel@tonic-gate 		}
4597*7c478bd9Sstevel@tonic-gate 	}
4598*7c478bd9Sstevel@tonic-gate 
4599*7c478bd9Sstevel@tonic-gate 	if (!error && sarg.vap->va_mask != 0)
4600*7c478bd9Sstevel@tonic-gate 		error = VOP_SETATTR(vp, sarg.vap, sarg.flag, cr, &ct);
4601*7c478bd9Sstevel@tonic-gate 
4602*7c478bd9Sstevel@tonic-gate 	/* restore AT_SIZE */
4603*7c478bd9Sstevel@tonic-gate 	if (saved_mask & AT_SIZE)
4604*7c478bd9Sstevel@tonic-gate 		sarg.vap->va_mask |= AT_SIZE;
4605*7c478bd9Sstevel@tonic-gate 
4606*7c478bd9Sstevel@tonic-gate 	/*
4607*7c478bd9Sstevel@tonic-gate 	 * If an ACL was being set, it has been delayed until now,
4608*7c478bd9Sstevel@tonic-gate 	 * in order to set the mode (via the VOP_SETATTR() above) first.
4609*7c478bd9Sstevel@tonic-gate 	 */
4610*7c478bd9Sstevel@tonic-gate 	if ((! error) && (fattrp->attrmask & FATTR4_ACL_MASK)) {
4611*7c478bd9Sstevel@tonic-gate 		int i;
4612*7c478bd9Sstevel@tonic-gate 
4613*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < NFS4_MAXNUM_ATTRS; i++)
4614*7c478bd9Sstevel@tonic-gate 			if (ntov.amap[i] == FATTR4_ACL)
4615*7c478bd9Sstevel@tonic-gate 				break;
4616*7c478bd9Sstevel@tonic-gate 		if (i < NFS4_MAXNUM_ATTRS) {
4617*7c478bd9Sstevel@tonic-gate 			error = (*nfs4_ntov_map[FATTR4_ACL].sv_getit)(
4618*7c478bd9Sstevel@tonic-gate 			    NFS4ATTR_SETIT, &sarg, &ntov.na[i]);
4619*7c478bd9Sstevel@tonic-gate 			if (error == 0) {
4620*7c478bd9Sstevel@tonic-gate 				*resp |= FATTR4_ACL_MASK;
4621*7c478bd9Sstevel@tonic-gate 			} else if (error == ENOTSUP) {
4622*7c478bd9Sstevel@tonic-gate 				(void) rfs4_verify_attr(&sarg, resp, &ntov);
4623*7c478bd9Sstevel@tonic-gate 				status = NFS4ERR_ATTRNOTSUPP;
4624*7c478bd9Sstevel@tonic-gate 				goto done;
4625*7c478bd9Sstevel@tonic-gate 			}
4626*7c478bd9Sstevel@tonic-gate 		} else {
4627*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug,
4628*7c478bd9Sstevel@tonic-gate 			    (CE_NOTE, "do_rfs4_op_setattr: "
4629*7c478bd9Sstevel@tonic-gate 			    "unable to find ACL in fattr4"));
4630*7c478bd9Sstevel@tonic-gate 			error = EINVAL;
4631*7c478bd9Sstevel@tonic-gate 		}
4632*7c478bd9Sstevel@tonic-gate 	}
4633*7c478bd9Sstevel@tonic-gate 
4634*7c478bd9Sstevel@tonic-gate 	if (error) {
4635*7c478bd9Sstevel@tonic-gate 		status = puterrno4(error);
4636*7c478bd9Sstevel@tonic-gate 
4637*7c478bd9Sstevel@tonic-gate 		/*
4638*7c478bd9Sstevel@tonic-gate 		 * Set the response bitmap when setattr failed.
4639*7c478bd9Sstevel@tonic-gate 		 * If VOP_SETATTR partially succeeded, test by doing a
4640*7c478bd9Sstevel@tonic-gate 		 * VOP_GETATTR on the object and comparing the data
4641*7c478bd9Sstevel@tonic-gate 		 * to the setattr arguments.
4642*7c478bd9Sstevel@tonic-gate 		 */
4643*7c478bd9Sstevel@tonic-gate 		(void) rfs4_verify_attr(&sarg, resp, &ntov);
4644*7c478bd9Sstevel@tonic-gate 	} else {
4645*7c478bd9Sstevel@tonic-gate 		/*
4646*7c478bd9Sstevel@tonic-gate 		 * Force modified metadata out to stable storage.
4647*7c478bd9Sstevel@tonic-gate 		 */
4648*7c478bd9Sstevel@tonic-gate 		(void) VOP_FSYNC(vp, FNODSYNC, cr);
4649*7c478bd9Sstevel@tonic-gate 		/*
4650*7c478bd9Sstevel@tonic-gate 		 * Set response bitmap
4651*7c478bd9Sstevel@tonic-gate 		 */
4652*7c478bd9Sstevel@tonic-gate 		nfs4_vmask_to_nmask(sarg.vap->va_mask, resp);
4653*7c478bd9Sstevel@tonic-gate 	}
4654*7c478bd9Sstevel@tonic-gate 
4655*7c478bd9Sstevel@tonic-gate /* Return early and already have a NFSv4 error */
4656*7c478bd9Sstevel@tonic-gate done:
4657*7c478bd9Sstevel@tonic-gate 	if (in_crit)
4658*7c478bd9Sstevel@tonic-gate 		nbl_end_crit(vp);
4659*7c478bd9Sstevel@tonic-gate 
4660*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_free(&ntov, &sarg);
4661*7c478bd9Sstevel@tonic-gate 
4662*7c478bd9Sstevel@tonic-gate 	return (status);
4663*7c478bd9Sstevel@tonic-gate }
4664*7c478bd9Sstevel@tonic-gate 
4665*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4666*7c478bd9Sstevel@tonic-gate static void
4667*7c478bd9Sstevel@tonic-gate rfs4_op_setattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
4668*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4669*7c478bd9Sstevel@tonic-gate {
4670*7c478bd9Sstevel@tonic-gate 	SETATTR4args *args = &argop->nfs_argop4_u.opsetattr;
4671*7c478bd9Sstevel@tonic-gate 	SETATTR4res *resp = &resop->nfs_resop4_u.opsetattr;
4672*7c478bd9Sstevel@tonic-gate 
4673*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
4674*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
4675*7c478bd9Sstevel@tonic-gate 		return;
4676*7c478bd9Sstevel@tonic-gate 	}
4677*7c478bd9Sstevel@tonic-gate 
4678*7c478bd9Sstevel@tonic-gate 	/*
4679*7c478bd9Sstevel@tonic-gate 	 * If there is an unshared filesystem mounted on this vnode,
4680*7c478bd9Sstevel@tonic-gate 	 * do not allow to setattr on this vnode.
4681*7c478bd9Sstevel@tonic-gate 	 */
4682*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(cs->vp)) {
4683*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
4684*7c478bd9Sstevel@tonic-gate 		return;
4685*7c478bd9Sstevel@tonic-gate 	}
4686*7c478bd9Sstevel@tonic-gate 
4687*7c478bd9Sstevel@tonic-gate 	resp->attrsset = 0;
4688*7c478bd9Sstevel@tonic-gate 
4689*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
4690*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
4691*7c478bd9Sstevel@tonic-gate 		return;
4692*7c478bd9Sstevel@tonic-gate 	}
4693*7c478bd9Sstevel@tonic-gate 
4694*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status =
4695*7c478bd9Sstevel@tonic-gate 		do_rfs4_op_setattr(&resp->attrsset, &args->obj_attributes, cs,
4696*7c478bd9Sstevel@tonic-gate 			&args->stateid);
4697*7c478bd9Sstevel@tonic-gate }
4698*7c478bd9Sstevel@tonic-gate 
4699*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4700*7c478bd9Sstevel@tonic-gate static void
4701*7c478bd9Sstevel@tonic-gate rfs4_op_verify(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
4702*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4703*7c478bd9Sstevel@tonic-gate {
4704*7c478bd9Sstevel@tonic-gate 	/*
4705*7c478bd9Sstevel@tonic-gate 	 * verify and nverify are exactly the same, except that nverify
4706*7c478bd9Sstevel@tonic-gate 	 * succeeds when some argument changed, and verify succeeds when
4707*7c478bd9Sstevel@tonic-gate 	 * when none changed.
4708*7c478bd9Sstevel@tonic-gate 	 */
4709*7c478bd9Sstevel@tonic-gate 
4710*7c478bd9Sstevel@tonic-gate 	VERIFY4args  *args = &argop->nfs_argop4_u.opverify;
4711*7c478bd9Sstevel@tonic-gate 	VERIFY4res *resp = &resop->nfs_resop4_u.opverify;
4712*7c478bd9Sstevel@tonic-gate 
4713*7c478bd9Sstevel@tonic-gate 	int error;
4714*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg sarg;
4715*7c478bd9Sstevel@tonic-gate 	struct statvfs64 sb;
4716*7c478bd9Sstevel@tonic-gate 	struct nfs4_ntov_table ntov;
4717*7c478bd9Sstevel@tonic-gate 
4718*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
4719*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
4720*7c478bd9Sstevel@tonic-gate 		return;
4721*7c478bd9Sstevel@tonic-gate 	}
4722*7c478bd9Sstevel@tonic-gate 
4723*7c478bd9Sstevel@tonic-gate 	sarg.sbp = &sb;
4724*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_init(&ntov);
4725*7c478bd9Sstevel@tonic-gate 	resp->status = do_rfs4_set_attrs(NULL, &args->obj_attributes, cs,
4726*7c478bd9Sstevel@tonic-gate 				&sarg, &ntov, NFS4ATTR_VERIT);
4727*7c478bd9Sstevel@tonic-gate 	if (resp->status != NFS4_OK) {
4728*7c478bd9Sstevel@tonic-gate 		/*
4729*7c478bd9Sstevel@tonic-gate 		 * do_rfs4_set_attrs will try to verify systemwide attrs,
4730*7c478bd9Sstevel@tonic-gate 		 * so could return -1 for "no match".
4731*7c478bd9Sstevel@tonic-gate 		 */
4732*7c478bd9Sstevel@tonic-gate 		if (resp->status == -1)
4733*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_NOT_SAME;
4734*7c478bd9Sstevel@tonic-gate 		goto done;
4735*7c478bd9Sstevel@tonic-gate 	}
4736*7c478bd9Sstevel@tonic-gate 	error = rfs4_verify_attr(&sarg, NULL, &ntov);
4737*7c478bd9Sstevel@tonic-gate 	switch (error) {
4738*7c478bd9Sstevel@tonic-gate 	case 0:
4739*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4_OK;
4740*7c478bd9Sstevel@tonic-gate 		break;
4741*7c478bd9Sstevel@tonic-gate 	case -1:
4742*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_NOT_SAME;
4743*7c478bd9Sstevel@tonic-gate 		break;
4744*7c478bd9Sstevel@tonic-gate 	default:
4745*7c478bd9Sstevel@tonic-gate 		resp->status = puterrno4(error);
4746*7c478bd9Sstevel@tonic-gate 		break;
4747*7c478bd9Sstevel@tonic-gate 	}
4748*7c478bd9Sstevel@tonic-gate done:
4749*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status;
4750*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_free(&ntov, &sarg);
4751*7c478bd9Sstevel@tonic-gate }
4752*7c478bd9Sstevel@tonic-gate 
4753*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4754*7c478bd9Sstevel@tonic-gate static void
4755*7c478bd9Sstevel@tonic-gate rfs4_op_nverify(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
4756*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4757*7c478bd9Sstevel@tonic-gate {
4758*7c478bd9Sstevel@tonic-gate 	/*
4759*7c478bd9Sstevel@tonic-gate 	 * verify and nverify are exactly the same, except that nverify
4760*7c478bd9Sstevel@tonic-gate 	 * succeeds when some argument changed, and verify succeeds when
4761*7c478bd9Sstevel@tonic-gate 	 * when none changed.
4762*7c478bd9Sstevel@tonic-gate 	 */
4763*7c478bd9Sstevel@tonic-gate 
4764*7c478bd9Sstevel@tonic-gate 	NVERIFY4args  *args = &argop->nfs_argop4_u.opnverify;
4765*7c478bd9Sstevel@tonic-gate 	NVERIFY4res *resp = &resop->nfs_resop4_u.opnverify;
4766*7c478bd9Sstevel@tonic-gate 
4767*7c478bd9Sstevel@tonic-gate 	int error;
4768*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg sarg;
4769*7c478bd9Sstevel@tonic-gate 	struct statvfs64 sb;
4770*7c478bd9Sstevel@tonic-gate 	struct nfs4_ntov_table ntov;
4771*7c478bd9Sstevel@tonic-gate 
4772*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
4773*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
4774*7c478bd9Sstevel@tonic-gate 		return;
4775*7c478bd9Sstevel@tonic-gate 	}
4776*7c478bd9Sstevel@tonic-gate 	sarg.sbp = &sb;
4777*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_init(&ntov);
4778*7c478bd9Sstevel@tonic-gate 	resp->status = do_rfs4_set_attrs(NULL, &args->obj_attributes, cs,
4779*7c478bd9Sstevel@tonic-gate 				&sarg, &ntov, NFS4ATTR_VERIT);
4780*7c478bd9Sstevel@tonic-gate 	if (resp->status != NFS4_OK) {
4781*7c478bd9Sstevel@tonic-gate 		/*
4782*7c478bd9Sstevel@tonic-gate 		 * do_rfs4_set_attrs will try to verify systemwide attrs,
4783*7c478bd9Sstevel@tonic-gate 		 * so could return -1 for "no match".
4784*7c478bd9Sstevel@tonic-gate 		 */
4785*7c478bd9Sstevel@tonic-gate 		if (resp->status == -1)
4786*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4_OK;
4787*7c478bd9Sstevel@tonic-gate 		goto done;
4788*7c478bd9Sstevel@tonic-gate 	}
4789*7c478bd9Sstevel@tonic-gate 	error = rfs4_verify_attr(&sarg, NULL, &ntov);
4790*7c478bd9Sstevel@tonic-gate 	switch (error) {
4791*7c478bd9Sstevel@tonic-gate 	case 0:
4792*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SAME;
4793*7c478bd9Sstevel@tonic-gate 		break;
4794*7c478bd9Sstevel@tonic-gate 	case -1:
4795*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4_OK;
4796*7c478bd9Sstevel@tonic-gate 		break;
4797*7c478bd9Sstevel@tonic-gate 	default:
4798*7c478bd9Sstevel@tonic-gate 		resp->status = puterrno4(error);
4799*7c478bd9Sstevel@tonic-gate 		break;
4800*7c478bd9Sstevel@tonic-gate 	}
4801*7c478bd9Sstevel@tonic-gate done:
4802*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status;
4803*7c478bd9Sstevel@tonic-gate 	nfs4_ntov_table_free(&ntov, &sarg);
4804*7c478bd9Sstevel@tonic-gate }
4805*7c478bd9Sstevel@tonic-gate 
4806*7c478bd9Sstevel@tonic-gate /*
4807*7c478bd9Sstevel@tonic-gate  * XXX - This should live in an NFS header file.
4808*7c478bd9Sstevel@tonic-gate  */
4809*7c478bd9Sstevel@tonic-gate #define	MAX_IOVECS	12
4810*7c478bd9Sstevel@tonic-gate 
4811*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4812*7c478bd9Sstevel@tonic-gate static void
4813*7c478bd9Sstevel@tonic-gate rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
4814*7c478bd9Sstevel@tonic-gate 	struct compound_state *cs)
4815*7c478bd9Sstevel@tonic-gate {
4816*7c478bd9Sstevel@tonic-gate 	WRITE4args  *args = &argop->nfs_argop4_u.opwrite;
4817*7c478bd9Sstevel@tonic-gate 	WRITE4res *resp = &resop->nfs_resop4_u.opwrite;
4818*7c478bd9Sstevel@tonic-gate 	int error;
4819*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
4820*7c478bd9Sstevel@tonic-gate 	struct vattr bva;
4821*7c478bd9Sstevel@tonic-gate 	u_offset_t rlimit;
4822*7c478bd9Sstevel@tonic-gate 	struct uio uio;
4823*7c478bd9Sstevel@tonic-gate 	struct iovec iov[MAX_IOVECS];
4824*7c478bd9Sstevel@tonic-gate 	struct iovec *iovp;
4825*7c478bd9Sstevel@tonic-gate 	int iovcnt;
4826*7c478bd9Sstevel@tonic-gate 	int ioflag;
4827*7c478bd9Sstevel@tonic-gate 	cred_t *savecred, *cr;
4828*7c478bd9Sstevel@tonic-gate 	bool_t *deleg = &cs->deleg;
4829*7c478bd9Sstevel@tonic-gate 	nfsstat4 stat;
4830*7c478bd9Sstevel@tonic-gate 	int in_crit = 0;
4831*7c478bd9Sstevel@tonic-gate 
4832*7c478bd9Sstevel@tonic-gate 	vp = cs->vp;
4833*7c478bd9Sstevel@tonic-gate 	if (vp == NULL) {
4834*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
4835*7c478bd9Sstevel@tonic-gate 		return;
4836*7c478bd9Sstevel@tonic-gate 	}
4837*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED) {
4838*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
4839*7c478bd9Sstevel@tonic-gate 		return;
4840*7c478bd9Sstevel@tonic-gate 	}
4841*7c478bd9Sstevel@tonic-gate 
4842*7c478bd9Sstevel@tonic-gate 	cr = cs->cr;
4843*7c478bd9Sstevel@tonic-gate 
4844*7c478bd9Sstevel@tonic-gate 	/*
4845*7c478bd9Sstevel@tonic-gate 	 * We have to enter the critical region before calling VOP_RWLOCK
4846*7c478bd9Sstevel@tonic-gate 	 * to avoid a deadlock with ufs.
4847*7c478bd9Sstevel@tonic-gate 	 */
4848*7c478bd9Sstevel@tonic-gate 	if (nbl_need_check(vp)) {
4849*7c478bd9Sstevel@tonic-gate 		nbl_start_crit(vp, RW_READER);
4850*7c478bd9Sstevel@tonic-gate 		in_crit = 1;
4851*7c478bd9Sstevel@tonic-gate 		if (nbl_conflict(vp, NBL_WRITE,
4852*7c478bd9Sstevel@tonic-gate 				args->offset, args->data_len, 0)) {
4853*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_LOCKED;
4854*7c478bd9Sstevel@tonic-gate 			goto out;
4855*7c478bd9Sstevel@tonic-gate 		}
4856*7c478bd9Sstevel@tonic-gate 	}
4857*7c478bd9Sstevel@tonic-gate 
4858*7c478bd9Sstevel@tonic-gate 	if ((stat = rfs4_check_stateid(FWRITE, vp, &args->stateid, FALSE,
4859*7c478bd9Sstevel@tonic-gate 					deleg, TRUE)) != NFS4_OK) {
4860*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = stat;
4861*7c478bd9Sstevel@tonic-gate 		goto out;
4862*7c478bd9Sstevel@tonic-gate 	}
4863*7c478bd9Sstevel@tonic-gate 
4864*7c478bd9Sstevel@tonic-gate 	bva.va_mask = AT_MODE | AT_UID;
4865*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(vp, &bva, 0, cr);
4866*7c478bd9Sstevel@tonic-gate 
4867*7c478bd9Sstevel@tonic-gate 	/*
4868*7c478bd9Sstevel@tonic-gate 	 * If we can't get the attributes, then we can't do the
4869*7c478bd9Sstevel@tonic-gate 	 * right access checking.  So, we'll fail the request.
4870*7c478bd9Sstevel@tonic-gate 	 */
4871*7c478bd9Sstevel@tonic-gate 	if (error) {
4872*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
4873*7c478bd9Sstevel@tonic-gate 		goto out;
4874*7c478bd9Sstevel@tonic-gate 	}
4875*7c478bd9Sstevel@tonic-gate 
4876*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, cs->vp, req)) {
4877*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ROFS;
4878*7c478bd9Sstevel@tonic-gate 		goto out;
4879*7c478bd9Sstevel@tonic-gate 	}
4880*7c478bd9Sstevel@tonic-gate 
4881*7c478bd9Sstevel@tonic-gate 	if (vp->v_type != VREG) {
4882*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status =
4883*7c478bd9Sstevel@tonic-gate 			((vp->v_type == VDIR) ? NFS4ERR_ISDIR : NFS4ERR_INVAL);
4884*7c478bd9Sstevel@tonic-gate 		goto out;
4885*7c478bd9Sstevel@tonic-gate 	}
4886*7c478bd9Sstevel@tonic-gate 
4887*7c478bd9Sstevel@tonic-gate 	if (crgetuid(cr) != bva.va_uid &&
4888*7c478bd9Sstevel@tonic-gate 	    (error = VOP_ACCESS(vp, VWRITE, 0, cr))) {
4889*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
4890*7c478bd9Sstevel@tonic-gate 		goto out;
4891*7c478bd9Sstevel@tonic-gate 	}
4892*7c478bd9Sstevel@tonic-gate 
4893*7c478bd9Sstevel@tonic-gate 	if (MANDLOCK(vp, bva.va_mode)) {
4894*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
4895*7c478bd9Sstevel@tonic-gate 		goto out;
4896*7c478bd9Sstevel@tonic-gate 	}
4897*7c478bd9Sstevel@tonic-gate 
4898*7c478bd9Sstevel@tonic-gate 	if (args->data_len == 0) {
4899*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
4900*7c478bd9Sstevel@tonic-gate 		resp->count = 0;
4901*7c478bd9Sstevel@tonic-gate 		resp->committed = args->stable;
4902*7c478bd9Sstevel@tonic-gate 		resp->writeverf = Write4verf;
4903*7c478bd9Sstevel@tonic-gate 		goto out;
4904*7c478bd9Sstevel@tonic-gate 	}
4905*7c478bd9Sstevel@tonic-gate 
4906*7c478bd9Sstevel@tonic-gate 	if (args->mblk != NULL) {
4907*7c478bd9Sstevel@tonic-gate 		mblk_t *m;
4908*7c478bd9Sstevel@tonic-gate 		uint_t bytes, round_len;
4909*7c478bd9Sstevel@tonic-gate 
4910*7c478bd9Sstevel@tonic-gate 		iovcnt = 0;
4911*7c478bd9Sstevel@tonic-gate 		bytes = 0;
4912*7c478bd9Sstevel@tonic-gate 		round_len = roundup(args->data_len, BYTES_PER_XDR_UNIT);
4913*7c478bd9Sstevel@tonic-gate 		for (m = args->mblk;
4914*7c478bd9Sstevel@tonic-gate 		    m != NULL && bytes < round_len;
4915*7c478bd9Sstevel@tonic-gate 		    m = m->b_cont) {
4916*7c478bd9Sstevel@tonic-gate 			iovcnt++;
4917*7c478bd9Sstevel@tonic-gate 			bytes += MBLKL(m);
4918*7c478bd9Sstevel@tonic-gate 		}
4919*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
4920*7c478bd9Sstevel@tonic-gate 		/* should have ended on an mblk boundary */
4921*7c478bd9Sstevel@tonic-gate 		if (bytes != round_len) {
4922*7c478bd9Sstevel@tonic-gate 			printf("bytes=0x%x, round_len=0x%x, req len=0x%x\n",
4923*7c478bd9Sstevel@tonic-gate 			    bytes, round_len, args->data_len);
4924*7c478bd9Sstevel@tonic-gate 			printf("args=%p, args->mblk=%p, m=%p", (void *)args,
4925*7c478bd9Sstevel@tonic-gate 			    (void *)args->mblk, (void *)m);
4926*7c478bd9Sstevel@tonic-gate 			ASSERT(bytes == round_len);
4927*7c478bd9Sstevel@tonic-gate 		}
4928*7c478bd9Sstevel@tonic-gate #endif
4929*7c478bd9Sstevel@tonic-gate 		if (iovcnt <= MAX_IOVECS) {
4930*7c478bd9Sstevel@tonic-gate 			iovp = iov;
4931*7c478bd9Sstevel@tonic-gate 		} else {
4932*7c478bd9Sstevel@tonic-gate 			iovp = kmem_alloc(sizeof (*iovp) * iovcnt, KM_SLEEP);
4933*7c478bd9Sstevel@tonic-gate 		}
4934*7c478bd9Sstevel@tonic-gate 		mblk_to_iov(args->mblk, iovcnt, iovp);
4935*7c478bd9Sstevel@tonic-gate 	} else {
4936*7c478bd9Sstevel@tonic-gate 		iovcnt = 1;
4937*7c478bd9Sstevel@tonic-gate 		iovp = iov;
4938*7c478bd9Sstevel@tonic-gate 		iovp->iov_base = args->data_val;
4939*7c478bd9Sstevel@tonic-gate 		iovp->iov_len = args->data_len;
4940*7c478bd9Sstevel@tonic-gate 	}
4941*7c478bd9Sstevel@tonic-gate 
4942*7c478bd9Sstevel@tonic-gate 	uio.uio_iov = iovp;
4943*7c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = iovcnt;
4944*7c478bd9Sstevel@tonic-gate 
4945*7c478bd9Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
4946*7c478bd9Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_DEFAULT;
4947*7c478bd9Sstevel@tonic-gate 	uio.uio_loffset = args->offset;
4948*7c478bd9Sstevel@tonic-gate 	uio.uio_resid = args->data_len;
4949*7c478bd9Sstevel@tonic-gate 	uio.uio_llimit = curproc->p_fsz_ctl;
4950*7c478bd9Sstevel@tonic-gate 	rlimit = uio.uio_llimit - args->offset;
4951*7c478bd9Sstevel@tonic-gate 	if (rlimit < (u_offset_t)uio.uio_resid)
4952*7c478bd9Sstevel@tonic-gate 		uio.uio_resid = (int)rlimit;
4953*7c478bd9Sstevel@tonic-gate 
4954*7c478bd9Sstevel@tonic-gate 	if (args->stable == UNSTABLE4)
4955*7c478bd9Sstevel@tonic-gate 		ioflag = 0;
4956*7c478bd9Sstevel@tonic-gate 	else if (args->stable == FILE_SYNC4)
4957*7c478bd9Sstevel@tonic-gate 		ioflag = FSYNC;
4958*7c478bd9Sstevel@tonic-gate 	else if (args->stable == DATA_SYNC4)
4959*7c478bd9Sstevel@tonic-gate 		ioflag = FDSYNC;
4960*7c478bd9Sstevel@tonic-gate 	else {
4961*7c478bd9Sstevel@tonic-gate 		if (iovp != iov)
4962*7c478bd9Sstevel@tonic-gate 			kmem_free(iovp, sizeof (*iovp) * iovcnt);
4963*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
4964*7c478bd9Sstevel@tonic-gate 		goto out;
4965*7c478bd9Sstevel@tonic-gate 	}
4966*7c478bd9Sstevel@tonic-gate 
4967*7c478bd9Sstevel@tonic-gate 	/*
4968*7c478bd9Sstevel@tonic-gate 	 * We're changing creds because VM may fault and we need
4969*7c478bd9Sstevel@tonic-gate 	 * the cred of the current thread to be used if quota
4970*7c478bd9Sstevel@tonic-gate 	 * checking is enabled.
4971*7c478bd9Sstevel@tonic-gate 	 */
4972*7c478bd9Sstevel@tonic-gate 	savecred = curthread->t_cred;
4973*7c478bd9Sstevel@tonic-gate 	curthread->t_cred = cr;
4974*7c478bd9Sstevel@tonic-gate 	error = do_io(FWRITE, vp, &uio, ioflag, cr);
4975*7c478bd9Sstevel@tonic-gate 	curthread->t_cred = savecred;
4976*7c478bd9Sstevel@tonic-gate 
4977*7c478bd9Sstevel@tonic-gate 	if (iovp != iov)
4978*7c478bd9Sstevel@tonic-gate 		kmem_free(iovp, sizeof (*iovp) * iovcnt);
4979*7c478bd9Sstevel@tonic-gate 
4980*7c478bd9Sstevel@tonic-gate 	if (error) {
4981*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
4982*7c478bd9Sstevel@tonic-gate 		goto out;
4983*7c478bd9Sstevel@tonic-gate 	}
4984*7c478bd9Sstevel@tonic-gate 
4985*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
4986*7c478bd9Sstevel@tonic-gate 	resp->count = args->data_len - uio.uio_resid;
4987*7c478bd9Sstevel@tonic-gate 
4988*7c478bd9Sstevel@tonic-gate 	if (ioflag == 0)
4989*7c478bd9Sstevel@tonic-gate 		resp->committed = UNSTABLE4;
4990*7c478bd9Sstevel@tonic-gate 	else
4991*7c478bd9Sstevel@tonic-gate 		resp->committed = FILE_SYNC4;
4992*7c478bd9Sstevel@tonic-gate 
4993*7c478bd9Sstevel@tonic-gate 	resp->writeverf = Write4verf;
4994*7c478bd9Sstevel@tonic-gate 
4995*7c478bd9Sstevel@tonic-gate out:
4996*7c478bd9Sstevel@tonic-gate 	if (in_crit)
4997*7c478bd9Sstevel@tonic-gate 		nbl_end_crit(vp);
4998*7c478bd9Sstevel@tonic-gate }
4999*7c478bd9Sstevel@tonic-gate 
5000*7c478bd9Sstevel@tonic-gate 
5001*7c478bd9Sstevel@tonic-gate /* XXX put in a header file */
5002*7c478bd9Sstevel@tonic-gate extern int	sec_svc_getcred(struct svc_req *, cred_t *,  caddr_t *, int *);
5003*7c478bd9Sstevel@tonic-gate 
5004*7c478bd9Sstevel@tonic-gate void
5005*7c478bd9Sstevel@tonic-gate rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi,
5006*7c478bd9Sstevel@tonic-gate 	struct svc_req *req, cred_t *cr)
5007*7c478bd9Sstevel@tonic-gate {
5008*7c478bd9Sstevel@tonic-gate 	uint_t i;
5009*7c478bd9Sstevel@tonic-gate 	struct compound_state cs;
5010*7c478bd9Sstevel@tonic-gate 
5011*7c478bd9Sstevel@tonic-gate 	rfs4_init_compound_state(&cs);
5012*7c478bd9Sstevel@tonic-gate 	/*
5013*7c478bd9Sstevel@tonic-gate 	 * Form a reply tag by copying over the reqeuest tag.
5014*7c478bd9Sstevel@tonic-gate 	 */
5015*7c478bd9Sstevel@tonic-gate 	resp->tag.utf8string_val =
5016*7c478bd9Sstevel@tonic-gate 				kmem_alloc(args->tag.utf8string_len, KM_SLEEP);
5017*7c478bd9Sstevel@tonic-gate 	resp->tag.utf8string_len = args->tag.utf8string_len;
5018*7c478bd9Sstevel@tonic-gate 	bcopy(args->tag.utf8string_val, resp->tag.utf8string_val,
5019*7c478bd9Sstevel@tonic-gate 					resp->tag.utf8string_len);
5020*7c478bd9Sstevel@tonic-gate 
5021*7c478bd9Sstevel@tonic-gate 	cs.statusp = &resp->status;
5022*7c478bd9Sstevel@tonic-gate 
5023*7c478bd9Sstevel@tonic-gate 	/*
5024*7c478bd9Sstevel@tonic-gate 	 * XXX for now, minorversion should be zero
5025*7c478bd9Sstevel@tonic-gate 	 */
5026*7c478bd9Sstevel@tonic-gate 	if (args->minorversion != NFS4_MINORVERSION) {
5027*7c478bd9Sstevel@tonic-gate 		resp->array_len = 0;
5028*7c478bd9Sstevel@tonic-gate 		resp->array = NULL;
5029*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_MINOR_VERS_MISMATCH;
5030*7c478bd9Sstevel@tonic-gate 		return;
5031*7c478bd9Sstevel@tonic-gate 	}
5032*7c478bd9Sstevel@tonic-gate 
5033*7c478bd9Sstevel@tonic-gate 	resp->array_len = args->array_len;
5034*7c478bd9Sstevel@tonic-gate 	resp->array = kmem_zalloc(args->array_len * sizeof (nfs_resop4),
5035*7c478bd9Sstevel@tonic-gate 		KM_SLEEP);
5036*7c478bd9Sstevel@tonic-gate 
5037*7c478bd9Sstevel@tonic-gate 	ASSERT(exi == NULL);
5038*7c478bd9Sstevel@tonic-gate 	ASSERT(cr == NULL);
5039*7c478bd9Sstevel@tonic-gate 
5040*7c478bd9Sstevel@tonic-gate 	cr = crget();
5041*7c478bd9Sstevel@tonic-gate 	ASSERT(cr != NULL);
5042*7c478bd9Sstevel@tonic-gate 
5043*7c478bd9Sstevel@tonic-gate 	if (sec_svc_getcred(req, cr, &cs.principal, &cs.nfsflavor) == 0) {
5044*7c478bd9Sstevel@tonic-gate 		crfree(cr);
5045*7c478bd9Sstevel@tonic-gate 		return;
5046*7c478bd9Sstevel@tonic-gate 	}
5047*7c478bd9Sstevel@tonic-gate 
5048*7c478bd9Sstevel@tonic-gate 	cs.basecr = cr;
5049*7c478bd9Sstevel@tonic-gate 
5050*7c478bd9Sstevel@tonic-gate 	cs.req = req;
5051*7c478bd9Sstevel@tonic-gate 
5052*7c478bd9Sstevel@tonic-gate 	/*
5053*7c478bd9Sstevel@tonic-gate 	 * For now, NFS4 compound processing must be protected by
5054*7c478bd9Sstevel@tonic-gate 	 * exported_lock because it can access more than one exportinfo
5055*7c478bd9Sstevel@tonic-gate 	 * per compound and share/unshare can now change multiple
5056*7c478bd9Sstevel@tonic-gate 	 * exinfo structs.  The NFS2/3 code only refs 1 exportinfo
5057*7c478bd9Sstevel@tonic-gate 	 * per proc (excluding public exinfo), and exi_count design
5058*7c478bd9Sstevel@tonic-gate 	 * is sufficient to protect concurrent execution of NFS2/3
5059*7c478bd9Sstevel@tonic-gate 	 * ops along with unexport.  This lock will be removed as
5060*7c478bd9Sstevel@tonic-gate 	 * part of the NFSv4 phase 2 namespace redesign work.
5061*7c478bd9Sstevel@tonic-gate 	 */
5062*7c478bd9Sstevel@tonic-gate 	rw_enter(&exported_lock, RW_READER);
5063*7c478bd9Sstevel@tonic-gate 
5064*7c478bd9Sstevel@tonic-gate 	/*
5065*7c478bd9Sstevel@tonic-gate 	 * If this is the first compound we've seen, we need to start all
5066*7c478bd9Sstevel@tonic-gate 	 * new instances' grace periods.
5067*7c478bd9Sstevel@tonic-gate 	 */
5068*7c478bd9Sstevel@tonic-gate 	if (rfs4_seen_first_compound == 0) {
5069*7c478bd9Sstevel@tonic-gate 		rfs4_grace_start_new();
5070*7c478bd9Sstevel@tonic-gate 		/*
5071*7c478bd9Sstevel@tonic-gate 		 * This must be set after rfs4_grace_start_new(), otherwise
5072*7c478bd9Sstevel@tonic-gate 		 * another thread could proceed past here before the former
5073*7c478bd9Sstevel@tonic-gate 		 * is finished.
5074*7c478bd9Sstevel@tonic-gate 		 */
5075*7c478bd9Sstevel@tonic-gate 		rfs4_seen_first_compound = 1;
5076*7c478bd9Sstevel@tonic-gate 	}
5077*7c478bd9Sstevel@tonic-gate 
5078*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < args->array_len && cs.cont; i++) {
5079*7c478bd9Sstevel@tonic-gate 		nfs_argop4 *argop;
5080*7c478bd9Sstevel@tonic-gate 		nfs_resop4 *resop;
5081*7c478bd9Sstevel@tonic-gate 		uint_t op;
5082*7c478bd9Sstevel@tonic-gate 
5083*7c478bd9Sstevel@tonic-gate 		argop = &args->array[i];
5084*7c478bd9Sstevel@tonic-gate 		resop = &resp->array[i];
5085*7c478bd9Sstevel@tonic-gate 		resop->resop = argop->argop;
5086*7c478bd9Sstevel@tonic-gate 		op = (uint_t)resop->resop;
5087*7c478bd9Sstevel@tonic-gate 
5088*7c478bd9Sstevel@tonic-gate 		if (op < rfsv4disp_cnt) {
5089*7c478bd9Sstevel@tonic-gate 			/*
5090*7c478bd9Sstevel@tonic-gate 			 * Count the individual ops here; NULL and COMPOUND
5091*7c478bd9Sstevel@tonic-gate 			 * are counted in common_dispatch()
5092*7c478bd9Sstevel@tonic-gate 			 */
5093*7c478bd9Sstevel@tonic-gate 			rfsproccnt_v4_ptr[op].value.ui64++;
5094*7c478bd9Sstevel@tonic-gate 
5095*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug > 1,
5096*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "Executing %s", rfs4_op_string[op]));
5097*7c478bd9Sstevel@tonic-gate 			(*rfsv4disptab[op].dis_proc)(argop, resop, req, &cs);
5098*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug > 1,
5099*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "%s returned %d",
5100*7c478bd9Sstevel@tonic-gate 				rfs4_op_string[op], *cs.statusp));
5101*7c478bd9Sstevel@tonic-gate 			if (*cs.statusp != NFS4_OK)
5102*7c478bd9Sstevel@tonic-gate 				cs.cont = FALSE;
5103*7c478bd9Sstevel@tonic-gate 		} else {
5104*7c478bd9Sstevel@tonic-gate 			/*
5105*7c478bd9Sstevel@tonic-gate 			 * This is effectively dead code since XDR code
5106*7c478bd9Sstevel@tonic-gate 			 * will have already returned BADXDR if op doesn't
5107*7c478bd9Sstevel@tonic-gate 			 * decode to legal value.  This only done for a
5108*7c478bd9Sstevel@tonic-gate 			 * day when XDR code doesn't verify v4 opcodes.
5109*7c478bd9Sstevel@tonic-gate 			 */
5110*7c478bd9Sstevel@tonic-gate 			op = OP_ILLEGAL;
5111*7c478bd9Sstevel@tonic-gate 			rfsproccnt_v4_ptr[OP_ILLEGAL_IDX].value.ui64++;
5112*7c478bd9Sstevel@tonic-gate 
5113*7c478bd9Sstevel@tonic-gate 			rfs4_op_illegal(argop, resop, req, &cs);
5114*7c478bd9Sstevel@tonic-gate 			cs.cont = FALSE;
5115*7c478bd9Sstevel@tonic-gate 		}
5116*7c478bd9Sstevel@tonic-gate 
5117*7c478bd9Sstevel@tonic-gate 		/*
5118*7c478bd9Sstevel@tonic-gate 		 * If not at last op, and if we are to stop, then
5119*7c478bd9Sstevel@tonic-gate 		 * compact the results array.
5120*7c478bd9Sstevel@tonic-gate 		 */
5121*7c478bd9Sstevel@tonic-gate 		if ((i + 1) < args->array_len && !cs.cont) {
5122*7c478bd9Sstevel@tonic-gate 			nfs_resop4 *new_res = kmem_alloc(
5123*7c478bd9Sstevel@tonic-gate 				(i+1) * sizeof (nfs_resop4), KM_SLEEP);
5124*7c478bd9Sstevel@tonic-gate 			bcopy(resp->array,
5125*7c478bd9Sstevel@tonic-gate 				new_res, (i+1) * sizeof (nfs_resop4));
5126*7c478bd9Sstevel@tonic-gate 			kmem_free(resp->array,
5127*7c478bd9Sstevel@tonic-gate 				args->array_len * sizeof (nfs_resop4));
5128*7c478bd9Sstevel@tonic-gate 
5129*7c478bd9Sstevel@tonic-gate 			resp->array_len =  i + 1;
5130*7c478bd9Sstevel@tonic-gate 			resp->array = new_res;
5131*7c478bd9Sstevel@tonic-gate 		}
5132*7c478bd9Sstevel@tonic-gate 	}
5133*7c478bd9Sstevel@tonic-gate 
5134*7c478bd9Sstevel@tonic-gate 	rw_exit(&exported_lock);
5135*7c478bd9Sstevel@tonic-gate 
5136*7c478bd9Sstevel@tonic-gate 	if (cs.vp)
5137*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs.vp);
5138*7c478bd9Sstevel@tonic-gate 	if (cs.saved_vp)
5139*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs.saved_vp);
5140*7c478bd9Sstevel@tonic-gate 	if (cs.saved_fh.nfs_fh4_val)
5141*7c478bd9Sstevel@tonic-gate 		kmem_free(cs.saved_fh.nfs_fh4_val, NFS4_FHSIZE);
5142*7c478bd9Sstevel@tonic-gate 
5143*7c478bd9Sstevel@tonic-gate 	if (cs.basecr)
5144*7c478bd9Sstevel@tonic-gate 		crfree(cs.basecr);
5145*7c478bd9Sstevel@tonic-gate 	if (cs.cr)
5146*7c478bd9Sstevel@tonic-gate 		crfree(cs.cr);
5147*7c478bd9Sstevel@tonic-gate }
5148*7c478bd9Sstevel@tonic-gate 
5149*7c478bd9Sstevel@tonic-gate /*
5150*7c478bd9Sstevel@tonic-gate  * XXX because of what appears to be duplicate calls to rfs4_compound_free
5151*7c478bd9Sstevel@tonic-gate  * XXX zero out the tag and array values. Need to investigate why the
5152*7c478bd9Sstevel@tonic-gate  * XXX calls occur, but at least prevent the panic for now.
5153*7c478bd9Sstevel@tonic-gate  */
5154*7c478bd9Sstevel@tonic-gate void
5155*7c478bd9Sstevel@tonic-gate rfs4_compound_free(COMPOUND4res *resp)
5156*7c478bd9Sstevel@tonic-gate {
5157*7c478bd9Sstevel@tonic-gate 	uint_t i;
5158*7c478bd9Sstevel@tonic-gate 
5159*7c478bd9Sstevel@tonic-gate 	if (resp->tag.utf8string_val) {
5160*7c478bd9Sstevel@tonic-gate 		UTF8STRING_FREE(resp->tag)
5161*7c478bd9Sstevel@tonic-gate 	}
5162*7c478bd9Sstevel@tonic-gate 
5163*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < resp->array_len; i++) {
5164*7c478bd9Sstevel@tonic-gate 		nfs_resop4 *resop;
5165*7c478bd9Sstevel@tonic-gate 		uint_t op;
5166*7c478bd9Sstevel@tonic-gate 
5167*7c478bd9Sstevel@tonic-gate 		resop = &resp->array[i];
5168*7c478bd9Sstevel@tonic-gate 		op = (uint_t)resop->resop;
5169*7c478bd9Sstevel@tonic-gate 		if (op < rfsv4disp_cnt) {
5170*7c478bd9Sstevel@tonic-gate 			(*rfsv4disptab[op].dis_resfree)(resop);
5171*7c478bd9Sstevel@tonic-gate 		}
5172*7c478bd9Sstevel@tonic-gate 	}
5173*7c478bd9Sstevel@tonic-gate 	if (resp->array != NULL) {
5174*7c478bd9Sstevel@tonic-gate 		kmem_free(resp->array, resp->array_len * sizeof (nfs_resop4));
5175*7c478bd9Sstevel@tonic-gate 	}
5176*7c478bd9Sstevel@tonic-gate }
5177*7c478bd9Sstevel@tonic-gate 
5178*7c478bd9Sstevel@tonic-gate /*
5179*7c478bd9Sstevel@tonic-gate  * Process the value of the compound request rpc flags, as a bit-AND
5180*7c478bd9Sstevel@tonic-gate  * of the individual per-op flags (idempotent, allowork, publicfh_ok)
5181*7c478bd9Sstevel@tonic-gate  */
5182*7c478bd9Sstevel@tonic-gate void
5183*7c478bd9Sstevel@tonic-gate rfs4_compound_flagproc(COMPOUND4args *args, int *flagp)
5184*7c478bd9Sstevel@tonic-gate {
5185*7c478bd9Sstevel@tonic-gate 	int i;
5186*7c478bd9Sstevel@tonic-gate 	int flag = RPC_ALL;
5187*7c478bd9Sstevel@tonic-gate 
5188*7c478bd9Sstevel@tonic-gate 	for (i = 0; flag && i < args->array_len; i++) {
5189*7c478bd9Sstevel@tonic-gate 		uint_t op;
5190*7c478bd9Sstevel@tonic-gate 
5191*7c478bd9Sstevel@tonic-gate 		op = (uint_t)args->array[i].argop;
5192*7c478bd9Sstevel@tonic-gate 
5193*7c478bd9Sstevel@tonic-gate 		if (op < rfsv4disp_cnt)
5194*7c478bd9Sstevel@tonic-gate 			flag &= rfsv4disptab[op].dis_flags;
5195*7c478bd9Sstevel@tonic-gate 		else
5196*7c478bd9Sstevel@tonic-gate 			flag = 0;
5197*7c478bd9Sstevel@tonic-gate 	}
5198*7c478bd9Sstevel@tonic-gate 	*flagp = flag;
5199*7c478bd9Sstevel@tonic-gate }
5200*7c478bd9Sstevel@tonic-gate 
5201*7c478bd9Sstevel@tonic-gate nfsstat4
5202*7c478bd9Sstevel@tonic-gate rfs4_client_sysid(rfs4_client_t *cp, sysid_t *sp)
5203*7c478bd9Sstevel@tonic-gate {
5204*7c478bd9Sstevel@tonic-gate 	nfsstat4 e;
5205*7c478bd9Sstevel@tonic-gate 
5206*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(cp->dbe);
5207*7c478bd9Sstevel@tonic-gate 
5208*7c478bd9Sstevel@tonic-gate 	if (cp->sysidt != LM_NOSYSID) {
5209*7c478bd9Sstevel@tonic-gate 		*sp = cp->sysidt;
5210*7c478bd9Sstevel@tonic-gate 		e = NFS4_OK;
5211*7c478bd9Sstevel@tonic-gate 
5212*7c478bd9Sstevel@tonic-gate 	} else if ((cp->sysidt = lm_alloc_sysidt()) != LM_NOSYSID) {
5213*7c478bd9Sstevel@tonic-gate 		*sp = cp->sysidt;
5214*7c478bd9Sstevel@tonic-gate 		e = NFS4_OK;
5215*7c478bd9Sstevel@tonic-gate 
5216*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug, (CE_NOTE,
5217*7c478bd9Sstevel@tonic-gate 			"rfs4_client_sysid: allocated 0x%x\n", *sp));
5218*7c478bd9Sstevel@tonic-gate 	} else
5219*7c478bd9Sstevel@tonic-gate 		e = NFS4ERR_DELAY;
5220*7c478bd9Sstevel@tonic-gate 
5221*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(cp->dbe);
5222*7c478bd9Sstevel@tonic-gate 	return (e);
5223*7c478bd9Sstevel@tonic-gate }
5224*7c478bd9Sstevel@tonic-gate 
5225*7c478bd9Sstevel@tonic-gate #if defined(DEBUG) && ! defined(lint)
5226*7c478bd9Sstevel@tonic-gate static void lock_print(char *str, int operation, struct flock64 *flk)
5227*7c478bd9Sstevel@tonic-gate {
5228*7c478bd9Sstevel@tonic-gate 	char *op, *type;
5229*7c478bd9Sstevel@tonic-gate 
5230*7c478bd9Sstevel@tonic-gate 	switch (operation) {
5231*7c478bd9Sstevel@tonic-gate 	case F_GETLK: op = "F_GETLK";
5232*7c478bd9Sstevel@tonic-gate 		break;
5233*7c478bd9Sstevel@tonic-gate 	case F_SETLK: op = "F_SETLK";
5234*7c478bd9Sstevel@tonic-gate 		break;
5235*7c478bd9Sstevel@tonic-gate 	default: op = "F_UNKNOWN";
5236*7c478bd9Sstevel@tonic-gate 		break;
5237*7c478bd9Sstevel@tonic-gate 	}
5238*7c478bd9Sstevel@tonic-gate 	switch (flk->l_type) {
5239*7c478bd9Sstevel@tonic-gate 	case F_UNLCK: type = "F_UNLCK";
5240*7c478bd9Sstevel@tonic-gate 		break;
5241*7c478bd9Sstevel@tonic-gate 	case F_RDLCK: type = "F_RDLCK";
5242*7c478bd9Sstevel@tonic-gate 		break;
5243*7c478bd9Sstevel@tonic-gate 	case F_WRLCK: type = "F_WRLCK";
5244*7c478bd9Sstevel@tonic-gate 		break;
5245*7c478bd9Sstevel@tonic-gate 	default: type = "F_UNKNOWN";
5246*7c478bd9Sstevel@tonic-gate 		break;
5247*7c478bd9Sstevel@tonic-gate 	}
5248*7c478bd9Sstevel@tonic-gate 
5249*7c478bd9Sstevel@tonic-gate 	ASSERT(flk->l_whence == 0);
5250*7c478bd9Sstevel@tonic-gate 	cmn_err(CE_NOTE, "%s:  %s, type = %s, off = %llx len = %llx pid = %d",
5251*7c478bd9Sstevel@tonic-gate 		str, op, type,
5252*7c478bd9Sstevel@tonic-gate 		(longlong_t)flk->l_start,
5253*7c478bd9Sstevel@tonic-gate 		flk->l_len ? (longlong_t)flk->l_len : ~0LL,
5254*7c478bd9Sstevel@tonic-gate 		flk->l_pid);
5255*7c478bd9Sstevel@tonic-gate }
5256*7c478bd9Sstevel@tonic-gate 
5257*7c478bd9Sstevel@tonic-gate #define	LOCK_PRINT(d, s, t, f) if (d) lock_print(s, t, f)
5258*7c478bd9Sstevel@tonic-gate #else
5259*7c478bd9Sstevel@tonic-gate #define	LOCK_PRINT(d, s, t, f)
5260*7c478bd9Sstevel@tonic-gate #endif
5261*7c478bd9Sstevel@tonic-gate 
5262*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5263*7c478bd9Sstevel@tonic-gate static bool_t
5264*7c478bd9Sstevel@tonic-gate creds_ok(cred_set_t cr_set, struct svc_req *req, struct compound_state *cs)
5265*7c478bd9Sstevel@tonic-gate {
5266*7c478bd9Sstevel@tonic-gate 	return (TRUE);
5267*7c478bd9Sstevel@tonic-gate }
5268*7c478bd9Sstevel@tonic-gate 
5269*7c478bd9Sstevel@tonic-gate /*
5270*7c478bd9Sstevel@tonic-gate  * Look up the pathname using the vp in cs as the directory vnode.
5271*7c478bd9Sstevel@tonic-gate  * cs->vp will be the vnode for the file on success
5272*7c478bd9Sstevel@tonic-gate  */
5273*7c478bd9Sstevel@tonic-gate 
5274*7c478bd9Sstevel@tonic-gate static nfsstat4
5275*7c478bd9Sstevel@tonic-gate rfs4_lookup(component4 *component, struct svc_req *req,
5276*7c478bd9Sstevel@tonic-gate 	    struct compound_state *cs)
5277*7c478bd9Sstevel@tonic-gate {
5278*7c478bd9Sstevel@tonic-gate 	char *nm;
5279*7c478bd9Sstevel@tonic-gate 	uint32_t len;
5280*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
5281*7c478bd9Sstevel@tonic-gate 
5282*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
5283*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOFILEHANDLE);
5284*7c478bd9Sstevel@tonic-gate 	}
5285*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VDIR) {
5286*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTDIR);
5287*7c478bd9Sstevel@tonic-gate 	}
5288*7c478bd9Sstevel@tonic-gate 
5289*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(component))
5290*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
5291*7c478bd9Sstevel@tonic-gate 
5292*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_fn(component, &len, NULL);
5293*7c478bd9Sstevel@tonic-gate 	if (nm == NULL) {
5294*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
5295*7c478bd9Sstevel@tonic-gate 	}
5296*7c478bd9Sstevel@tonic-gate 
5297*7c478bd9Sstevel@tonic-gate 	if (len > MAXNAMELEN) {
5298*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, len);
5299*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NAMETOOLONG);
5300*7c478bd9Sstevel@tonic-gate 	}
5301*7c478bd9Sstevel@tonic-gate 
5302*7c478bd9Sstevel@tonic-gate 	status = do_rfs4_op_lookup(nm, len, req, cs);
5303*7c478bd9Sstevel@tonic-gate 
5304*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, len);
5305*7c478bd9Sstevel@tonic-gate 
5306*7c478bd9Sstevel@tonic-gate 	return (status);
5307*7c478bd9Sstevel@tonic-gate }
5308*7c478bd9Sstevel@tonic-gate 
5309*7c478bd9Sstevel@tonic-gate static nfsstat4
5310*7c478bd9Sstevel@tonic-gate rfs4_lookupfile(component4 *component, struct svc_req *req,
5311*7c478bd9Sstevel@tonic-gate 		struct compound_state *cs, uint32_t access,
5312*7c478bd9Sstevel@tonic-gate 		change_info4 *cinfo)
5313*7c478bd9Sstevel@tonic-gate {
5314*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
5315*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp = cs->vp;
5316*7c478bd9Sstevel@tonic-gate 	vattr_t bva, ava, fva;
5317*7c478bd9Sstevel@tonic-gate 	int error;
5318*7c478bd9Sstevel@tonic-gate 
5319*7c478bd9Sstevel@tonic-gate 	/* Get "before" change value */
5320*7c478bd9Sstevel@tonic-gate 	bva.va_mask = AT_CTIME|AT_SEQ;
5321*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(dvp, &bva, 0, cs->cr);
5322*7c478bd9Sstevel@tonic-gate 	if (error)
5323*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
5324*7c478bd9Sstevel@tonic-gate 
5325*7c478bd9Sstevel@tonic-gate 	/* rfs4_lookup may VN_RELE directory */
5326*7c478bd9Sstevel@tonic-gate 	VN_HOLD(dvp);
5327*7c478bd9Sstevel@tonic-gate 
5328*7c478bd9Sstevel@tonic-gate 	status = rfs4_lookup(component, req, cs);
5329*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
5330*7c478bd9Sstevel@tonic-gate 		VN_RELE(dvp);
5331*7c478bd9Sstevel@tonic-gate 		return (status);
5332*7c478bd9Sstevel@tonic-gate 	}
5333*7c478bd9Sstevel@tonic-gate 
5334*7c478bd9Sstevel@tonic-gate 	/*
5335*7c478bd9Sstevel@tonic-gate 	 * Get "after" change value, if it fails, simply return the
5336*7c478bd9Sstevel@tonic-gate 	 * before value.
5337*7c478bd9Sstevel@tonic-gate 	 */
5338*7c478bd9Sstevel@tonic-gate 	ava.va_mask = AT_CTIME|AT_SEQ;
5339*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &ava, 0, cs->cr)) {
5340*7c478bd9Sstevel@tonic-gate 		ava.va_ctime = bva.va_ctime;
5341*7c478bd9Sstevel@tonic-gate 		ava.va_seq = 0;
5342*7c478bd9Sstevel@tonic-gate 	}
5343*7c478bd9Sstevel@tonic-gate 	VN_RELE(dvp);
5344*7c478bd9Sstevel@tonic-gate 
5345*7c478bd9Sstevel@tonic-gate 	/*
5346*7c478bd9Sstevel@tonic-gate 	 * Validate the file is a file
5347*7c478bd9Sstevel@tonic-gate 	 */
5348*7c478bd9Sstevel@tonic-gate 	fva.va_mask = AT_TYPE|AT_MODE;
5349*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(cs->vp, &fva, 0, cs->cr);
5350*7c478bd9Sstevel@tonic-gate 	if (error)
5351*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
5352*7c478bd9Sstevel@tonic-gate 
5353*7c478bd9Sstevel@tonic-gate 	if (fva.va_type != VREG) {
5354*7c478bd9Sstevel@tonic-gate 		if (fva.va_type == VDIR)
5355*7c478bd9Sstevel@tonic-gate 			return (NFS4ERR_ISDIR);
5356*7c478bd9Sstevel@tonic-gate 		if (fva.va_type == VLNK)
5357*7c478bd9Sstevel@tonic-gate 			return (NFS4ERR_SYMLINK);
5358*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
5359*7c478bd9Sstevel@tonic-gate 	}
5360*7c478bd9Sstevel@tonic-gate 
5361*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(cinfo->before, bva.va_ctime);
5362*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(cinfo->after, ava.va_ctime);
5363*7c478bd9Sstevel@tonic-gate 
5364*7c478bd9Sstevel@tonic-gate 	/*
5365*7c478bd9Sstevel@tonic-gate 	 * It is undefined if VOP_LOOKUP will change va_seq, so
5366*7c478bd9Sstevel@tonic-gate 	 * cinfo.atomic = TRUE only if we have
5367*7c478bd9Sstevel@tonic-gate 	 * non-zero va_seq's, and they have not changed.
5368*7c478bd9Sstevel@tonic-gate 	 */
5369*7c478bd9Sstevel@tonic-gate 	if (bva.va_seq && ava.va_seq && ava.va_seq == bva.va_seq)
5370*7c478bd9Sstevel@tonic-gate 		cinfo->atomic = TRUE;
5371*7c478bd9Sstevel@tonic-gate 	else
5372*7c478bd9Sstevel@tonic-gate 		cinfo->atomic = FALSE;
5373*7c478bd9Sstevel@tonic-gate 
5374*7c478bd9Sstevel@tonic-gate 	/* Check for mandatory locking */
5375*7c478bd9Sstevel@tonic-gate 	cs->mandlock = MANDLOCK(cs->vp, fva.va_mode);
5376*7c478bd9Sstevel@tonic-gate 	return (check_open_access(access, cs, req));
5377*7c478bd9Sstevel@tonic-gate }
5378*7c478bd9Sstevel@tonic-gate 
5379*7c478bd9Sstevel@tonic-gate static nfsstat4
5380*7c478bd9Sstevel@tonic-gate create_vnode(vnode_t *dvp, char *nm,  vattr_t *vap, createmode4 mode,
5381*7c478bd9Sstevel@tonic-gate 	    timespec32_t *mtime, cred_t *cr, vnode_t **vpp, bool_t *created)
5382*7c478bd9Sstevel@tonic-gate {
5383*7c478bd9Sstevel@tonic-gate 	int error;
5384*7c478bd9Sstevel@tonic-gate 	nfsstat4 status = NFS4_OK;
5385*7c478bd9Sstevel@tonic-gate 	vattr_t va;
5386*7c478bd9Sstevel@tonic-gate 
5387*7c478bd9Sstevel@tonic-gate tryagain:
5388*7c478bd9Sstevel@tonic-gate 
5389*7c478bd9Sstevel@tonic-gate 	/*
5390*7c478bd9Sstevel@tonic-gate 	 * The file open mode used is VWRITE.  If the client needs
5391*7c478bd9Sstevel@tonic-gate 	 * some other semantic, then it should do the access checking
5392*7c478bd9Sstevel@tonic-gate 	 * itself.  It would have been nice to have the file open mode
5393*7c478bd9Sstevel@tonic-gate 	 * passed as part of the arguments.
5394*7c478bd9Sstevel@tonic-gate 	 */
5395*7c478bd9Sstevel@tonic-gate 
5396*7c478bd9Sstevel@tonic-gate 	*created = TRUE;
5397*7c478bd9Sstevel@tonic-gate 	error = VOP_CREATE(dvp, nm, vap, EXCL, VWRITE, vpp, cr, 0);
5398*7c478bd9Sstevel@tonic-gate 
5399*7c478bd9Sstevel@tonic-gate 	if (error) {
5400*7c478bd9Sstevel@tonic-gate 		*created = FALSE;
5401*7c478bd9Sstevel@tonic-gate 
5402*7c478bd9Sstevel@tonic-gate 		/*
5403*7c478bd9Sstevel@tonic-gate 		 * If we got something other than file already exists
5404*7c478bd9Sstevel@tonic-gate 		 * then just return this error.  Otherwise, we got
5405*7c478bd9Sstevel@tonic-gate 		 * EEXIST.  If we were doing a GUARDED create, then
5406*7c478bd9Sstevel@tonic-gate 		 * just return this error.  Otherwise, we need to
5407*7c478bd9Sstevel@tonic-gate 		 * make sure that this wasn't a duplicate of an
5408*7c478bd9Sstevel@tonic-gate 		 * exclusive create request.
5409*7c478bd9Sstevel@tonic-gate 		 *
5410*7c478bd9Sstevel@tonic-gate 		 * The assumption is made that a non-exclusive create
5411*7c478bd9Sstevel@tonic-gate 		 * request will never return EEXIST.
5412*7c478bd9Sstevel@tonic-gate 		 */
5413*7c478bd9Sstevel@tonic-gate 
5414*7c478bd9Sstevel@tonic-gate 		if (error != EEXIST || mode == GUARDED4) {
5415*7c478bd9Sstevel@tonic-gate 			status = puterrno4(error);
5416*7c478bd9Sstevel@tonic-gate 			return (status);
5417*7c478bd9Sstevel@tonic-gate 		}
5418*7c478bd9Sstevel@tonic-gate 		error = VOP_LOOKUP(dvp, nm, vpp, NULL, 0, NULL, cr);
5419*7c478bd9Sstevel@tonic-gate 
5420*7c478bd9Sstevel@tonic-gate 		if (error) {
5421*7c478bd9Sstevel@tonic-gate 			/*
5422*7c478bd9Sstevel@tonic-gate 			 * We couldn't find the file that we thought that
5423*7c478bd9Sstevel@tonic-gate 			 * we just created.  So, we'll just try creating
5424*7c478bd9Sstevel@tonic-gate 			 * it again.
5425*7c478bd9Sstevel@tonic-gate 			 */
5426*7c478bd9Sstevel@tonic-gate 			if (error == ENOENT)
5427*7c478bd9Sstevel@tonic-gate 				goto tryagain;
5428*7c478bd9Sstevel@tonic-gate 
5429*7c478bd9Sstevel@tonic-gate 			status = puterrno4(error);
5430*7c478bd9Sstevel@tonic-gate 			return (status);
5431*7c478bd9Sstevel@tonic-gate 		}
5432*7c478bd9Sstevel@tonic-gate 
5433*7c478bd9Sstevel@tonic-gate 		VN_SETPATH(rootdir, dvp, *vpp, nm, strlen(nm));
5434*7c478bd9Sstevel@tonic-gate 
5435*7c478bd9Sstevel@tonic-gate 		if (mode == UNCHECKED4) {
5436*7c478bd9Sstevel@tonic-gate 			/* existing object must be regular file */
5437*7c478bd9Sstevel@tonic-gate 			if ((*vpp)->v_type != VREG) {
5438*7c478bd9Sstevel@tonic-gate 				if ((*vpp)->v_type == VDIR)
5439*7c478bd9Sstevel@tonic-gate 					status = NFS4ERR_ISDIR;
5440*7c478bd9Sstevel@tonic-gate 				else if ((*vpp)->v_type == VLNK)
5441*7c478bd9Sstevel@tonic-gate 					status = NFS4ERR_SYMLINK;
5442*7c478bd9Sstevel@tonic-gate 				else
5443*7c478bd9Sstevel@tonic-gate 					status = NFS4ERR_INVAL;
5444*7c478bd9Sstevel@tonic-gate 				VN_RELE(*vpp);
5445*7c478bd9Sstevel@tonic-gate 				return (status);
5446*7c478bd9Sstevel@tonic-gate 			}
5447*7c478bd9Sstevel@tonic-gate 
5448*7c478bd9Sstevel@tonic-gate 			return (NFS4_OK);
5449*7c478bd9Sstevel@tonic-gate 		}
5450*7c478bd9Sstevel@tonic-gate 
5451*7c478bd9Sstevel@tonic-gate 		/* Check for duplicate request */
5452*7c478bd9Sstevel@tonic-gate 		ASSERT(mtime != 0);
5453*7c478bd9Sstevel@tonic-gate 		va.va_mask = AT_MTIME;
5454*7c478bd9Sstevel@tonic-gate 		error = VOP_GETATTR(*vpp, &va, 0, cr);
5455*7c478bd9Sstevel@tonic-gate 		if (!error) {
5456*7c478bd9Sstevel@tonic-gate 			/* We found the file */
5457*7c478bd9Sstevel@tonic-gate 			if (va.va_mtime.tv_sec != mtime->tv_sec ||
5458*7c478bd9Sstevel@tonic-gate 			    va.va_mtime.tv_nsec != mtime->tv_nsec) {
5459*7c478bd9Sstevel@tonic-gate 				/* but its not our creation */
5460*7c478bd9Sstevel@tonic-gate 				VN_RELE(*vpp);
5461*7c478bd9Sstevel@tonic-gate 				return (NFS4ERR_EXIST);
5462*7c478bd9Sstevel@tonic-gate 			}
5463*7c478bd9Sstevel@tonic-gate 			*created = TRUE; /* retrans of create == created */
5464*7c478bd9Sstevel@tonic-gate 			return (NFS4_OK);
5465*7c478bd9Sstevel@tonic-gate 		}
5466*7c478bd9Sstevel@tonic-gate 		VN_RELE(*vpp);
5467*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_EXIST);
5468*7c478bd9Sstevel@tonic-gate 	}
5469*7c478bd9Sstevel@tonic-gate 
5470*7c478bd9Sstevel@tonic-gate 	return (NFS4_OK);
5471*7c478bd9Sstevel@tonic-gate }
5472*7c478bd9Sstevel@tonic-gate 
5473*7c478bd9Sstevel@tonic-gate static nfsstat4
5474*7c478bd9Sstevel@tonic-gate check_open_access(uint32_t access,
5475*7c478bd9Sstevel@tonic-gate 		struct compound_state *cs, struct svc_req *req)
5476*7c478bd9Sstevel@tonic-gate {
5477*7c478bd9Sstevel@tonic-gate 	int error;
5478*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
5479*7c478bd9Sstevel@tonic-gate 	bool_t readonly;
5480*7c478bd9Sstevel@tonic-gate 	cred_t *cr = cs->cr;
5481*7c478bd9Sstevel@tonic-gate 
5482*7c478bd9Sstevel@tonic-gate 	/* For now we don't allow mandatory locking as per V2/V3 */
5483*7c478bd9Sstevel@tonic-gate 	if (cs->access == CS_ACCESS_DENIED || cs->mandlock) {
5484*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ACCESS);
5485*7c478bd9Sstevel@tonic-gate 	}
5486*7c478bd9Sstevel@tonic-gate 
5487*7c478bd9Sstevel@tonic-gate 	vp = cs->vp;
5488*7c478bd9Sstevel@tonic-gate 	ASSERT(cr != NULL && vp->v_type == VREG);
5489*7c478bd9Sstevel@tonic-gate 
5490*7c478bd9Sstevel@tonic-gate 	/*
5491*7c478bd9Sstevel@tonic-gate 	 * If the file system is exported read only and we are trying
5492*7c478bd9Sstevel@tonic-gate 	 * to open for write, then return NFS4ERR_ROFS
5493*7c478bd9Sstevel@tonic-gate 	 */
5494*7c478bd9Sstevel@tonic-gate 
5495*7c478bd9Sstevel@tonic-gate 	readonly = rdonly4(cs->exi, cs->vp, req);
5496*7c478bd9Sstevel@tonic-gate 
5497*7c478bd9Sstevel@tonic-gate 	if ((access & OPEN4_SHARE_ACCESS_WRITE) && readonly)
5498*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ROFS);
5499*7c478bd9Sstevel@tonic-gate 
5500*7c478bd9Sstevel@tonic-gate 	if (access & OPEN4_SHARE_ACCESS_READ) {
5501*7c478bd9Sstevel@tonic-gate 		if ((VOP_ACCESS(vp, VREAD, 0, cr) != 0) &&
5502*7c478bd9Sstevel@tonic-gate 		    (VOP_ACCESS(vp, VEXEC, 0, cr) != 0)) {
5503*7c478bd9Sstevel@tonic-gate 			return (NFS4ERR_ACCESS);
5504*7c478bd9Sstevel@tonic-gate 		}
5505*7c478bd9Sstevel@tonic-gate 	}
5506*7c478bd9Sstevel@tonic-gate 
5507*7c478bd9Sstevel@tonic-gate 	if (access & OPEN4_SHARE_ACCESS_WRITE) {
5508*7c478bd9Sstevel@tonic-gate 		error = VOP_ACCESS(vp, VWRITE, 0, cr);
5509*7c478bd9Sstevel@tonic-gate 		if (error)
5510*7c478bd9Sstevel@tonic-gate 			return (NFS4ERR_ACCESS);
5511*7c478bd9Sstevel@tonic-gate 	}
5512*7c478bd9Sstevel@tonic-gate 
5513*7c478bd9Sstevel@tonic-gate 	return (NFS4_OK);
5514*7c478bd9Sstevel@tonic-gate }
5515*7c478bd9Sstevel@tonic-gate 
5516*7c478bd9Sstevel@tonic-gate static nfsstat4
5517*7c478bd9Sstevel@tonic-gate rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs,
5518*7c478bd9Sstevel@tonic-gate 		change_info4 *cinfo, bitmap4 *attrset, clientid4 clientid)
5519*7c478bd9Sstevel@tonic-gate {
5520*7c478bd9Sstevel@tonic-gate 	struct nfs4_svgetit_arg sarg;
5521*7c478bd9Sstevel@tonic-gate 	struct nfs4_ntov_table ntov;
5522*7c478bd9Sstevel@tonic-gate 
5523*7c478bd9Sstevel@tonic-gate 	bool_t ntov_table_init = FALSE;
5524*7c478bd9Sstevel@tonic-gate 	struct statvfs64 sb;
5525*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
5526*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
5527*7c478bd9Sstevel@tonic-gate 	vattr_t bva, ava, iva, cva, *vap;
5528*7c478bd9Sstevel@tonic-gate 	vnode_t *dvp;
5529*7c478bd9Sstevel@tonic-gate 	timespec32_t *mtime;
5530*7c478bd9Sstevel@tonic-gate 	char *nm = NULL;
5531*7c478bd9Sstevel@tonic-gate 	uint_t buflen;
5532*7c478bd9Sstevel@tonic-gate 	bool_t created;
5533*7c478bd9Sstevel@tonic-gate 	bool_t setsize = FALSE;
5534*7c478bd9Sstevel@tonic-gate 	len_t reqsize;
5535*7c478bd9Sstevel@tonic-gate 	int error;
5536*7c478bd9Sstevel@tonic-gate 	bool_t trunc;
5537*7c478bd9Sstevel@tonic-gate 	caller_context_t ct;
5538*7c478bd9Sstevel@tonic-gate 	component4 *component;
5539*7c478bd9Sstevel@tonic-gate 
5540*7c478bd9Sstevel@tonic-gate 	sarg.sbp = &sb;
5541*7c478bd9Sstevel@tonic-gate 
5542*7c478bd9Sstevel@tonic-gate 	dvp = cs->vp;
5543*7c478bd9Sstevel@tonic-gate 
5544*7c478bd9Sstevel@tonic-gate 	/* Check if the file system is read only */
5545*7c478bd9Sstevel@tonic-gate 	if (rdonly4(cs->exi, dvp, req))
5546*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_ROFS);
5547*7c478bd9Sstevel@tonic-gate 
5548*7c478bd9Sstevel@tonic-gate 	/*
5549*7c478bd9Sstevel@tonic-gate 	 * Get the last component of path name in nm. cs will reference
5550*7c478bd9Sstevel@tonic-gate 	 * the including directory on success.
5551*7c478bd9Sstevel@tonic-gate 	 */
5552*7c478bd9Sstevel@tonic-gate 	component = &args->open_claim4_u.file;
5553*7c478bd9Sstevel@tonic-gate 	if (!utf8_dir_verify(component))
5554*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
5555*7c478bd9Sstevel@tonic-gate 
5556*7c478bd9Sstevel@tonic-gate 	nm = utf8_to_fn(component, &buflen, NULL);
5557*7c478bd9Sstevel@tonic-gate 
5558*7c478bd9Sstevel@tonic-gate 	if (nm == NULL)
5559*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_RESOURCE);
5560*7c478bd9Sstevel@tonic-gate 
5561*7c478bd9Sstevel@tonic-gate 	if (buflen > MAXNAMELEN) {
5562*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, buflen);
5563*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NAMETOOLONG);
5564*7c478bd9Sstevel@tonic-gate 	}
5565*7c478bd9Sstevel@tonic-gate 
5566*7c478bd9Sstevel@tonic-gate 	bva.va_mask = AT_TYPE|AT_CTIME|AT_SEQ;
5567*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(dvp, &bva, 0, cs->cr);
5568*7c478bd9Sstevel@tonic-gate 	if (error) {
5569*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, buflen);
5570*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
5571*7c478bd9Sstevel@tonic-gate 	}
5572*7c478bd9Sstevel@tonic-gate 
5573*7c478bd9Sstevel@tonic-gate 	if (bva.va_type != VDIR) {
5574*7c478bd9Sstevel@tonic-gate 		kmem_free(nm, buflen);
5575*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_NOTDIR);
5576*7c478bd9Sstevel@tonic-gate 	}
5577*7c478bd9Sstevel@tonic-gate 
5578*7c478bd9Sstevel@tonic-gate 	NFS4_SET_FATTR4_CHANGE(cinfo->before, bva.va_ctime)
5579*7c478bd9Sstevel@tonic-gate 
5580*7c478bd9Sstevel@tonic-gate 	switch (args->mode) {
5581*7c478bd9Sstevel@tonic-gate 	case GUARDED4:
5582*7c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
5583*7c478bd9Sstevel@tonic-gate 	case UNCHECKED4:
5584*7c478bd9Sstevel@tonic-gate 		nfs4_ntov_table_init(&ntov);
5585*7c478bd9Sstevel@tonic-gate 		ntov_table_init = TRUE;
5586*7c478bd9Sstevel@tonic-gate 
5587*7c478bd9Sstevel@tonic-gate 		*attrset = 0;
5588*7c478bd9Sstevel@tonic-gate 		status = do_rfs4_set_attrs(attrset,
5589*7c478bd9Sstevel@tonic-gate 					&args->createhow4_u.createattrs,
5590*7c478bd9Sstevel@tonic-gate 					cs, &sarg, &ntov, NFS4ATTR_SETIT);
5591*7c478bd9Sstevel@tonic-gate 
5592*7c478bd9Sstevel@tonic-gate 		if (status == NFS4_OK && (sarg.vap->va_mask & AT_TYPE) &&
5593*7c478bd9Sstevel@tonic-gate 		    sarg.vap->va_type != VREG) {
5594*7c478bd9Sstevel@tonic-gate 			if (sarg.vap->va_type == VDIR)
5595*7c478bd9Sstevel@tonic-gate 				status = NFS4ERR_ISDIR;
5596*7c478bd9Sstevel@tonic-gate 			else if (sarg.vap->va_type == VLNK)
5597*7c478bd9Sstevel@tonic-gate 				status = NFS4ERR_SYMLINK;
5598*7c478bd9Sstevel@tonic-gate 			else
5599*7c478bd9Sstevel@tonic-gate 				status = NFS4ERR_INVAL;
5600*7c478bd9Sstevel@tonic-gate 		}
5601*7c478bd9Sstevel@tonic-gate 
5602*7c478bd9Sstevel@tonic-gate 		if (status != NFS4_OK) {
5603*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, buflen);
5604*7c478bd9Sstevel@tonic-gate 			nfs4_ntov_table_free(&ntov, &sarg);
5605*7c478bd9Sstevel@tonic-gate 			*attrset = 0;
5606*7c478bd9Sstevel@tonic-gate 			return (status);
5607*7c478bd9Sstevel@tonic-gate 		}
5608*7c478bd9Sstevel@tonic-gate 
5609*7c478bd9Sstevel@tonic-gate 		vap = sarg.vap;
5610*7c478bd9Sstevel@tonic-gate 		vap->va_type = VREG;
5611*7c478bd9Sstevel@tonic-gate 		vap->va_mask |= AT_TYPE;
5612*7c478bd9Sstevel@tonic-gate 
5613*7c478bd9Sstevel@tonic-gate 		if ((vap->va_mask & AT_MODE) == 0) {
5614*7c478bd9Sstevel@tonic-gate 			vap->va_mask |= AT_MODE;
5615*7c478bd9Sstevel@tonic-gate 			vap->va_mode = (mode_t)0600;
5616*7c478bd9Sstevel@tonic-gate 		}
5617*7c478bd9Sstevel@tonic-gate 
5618*7c478bd9Sstevel@tonic-gate 		if (vap->va_mask & AT_SIZE) {
5619*7c478bd9Sstevel@tonic-gate 
5620*7c478bd9Sstevel@tonic-gate 			/* Disallow create with a non-zero size */
5621*7c478bd9Sstevel@tonic-gate 
5622*7c478bd9Sstevel@tonic-gate 			if ((reqsize = sarg.vap->va_size) != 0) {
5623*7c478bd9Sstevel@tonic-gate 				kmem_free(nm, buflen);
5624*7c478bd9Sstevel@tonic-gate 				nfs4_ntov_table_free(&ntov, &sarg);
5625*7c478bd9Sstevel@tonic-gate 				*attrset = 0;
5626*7c478bd9Sstevel@tonic-gate 				return (NFS4ERR_INVAL);
5627*7c478bd9Sstevel@tonic-gate 			}
5628*7c478bd9Sstevel@tonic-gate 			setsize = TRUE;
5629*7c478bd9Sstevel@tonic-gate 		}
5630*7c478bd9Sstevel@tonic-gate 		break;
5631*7c478bd9Sstevel@tonic-gate 
5632*7c478bd9Sstevel@tonic-gate 	case EXCLUSIVE4:
5633*7c478bd9Sstevel@tonic-gate 		/* prohibit EXCL create of named attributes */
5634*7c478bd9Sstevel@tonic-gate 		if (dvp->v_flag & V_XATTRDIR) {
5635*7c478bd9Sstevel@tonic-gate 			kmem_free(nm, buflen);
5636*7c478bd9Sstevel@tonic-gate 			*attrset = 0;
5637*7c478bd9Sstevel@tonic-gate 			return (NFS4ERR_INVAL);
5638*7c478bd9Sstevel@tonic-gate 		}
5639*7c478bd9Sstevel@tonic-gate 
5640*7c478bd9Sstevel@tonic-gate 		cva.va_mask = AT_TYPE | AT_MTIME | AT_MODE;
5641*7c478bd9Sstevel@tonic-gate 		cva.va_type = VREG;
5642*7c478bd9Sstevel@tonic-gate 		/*
5643*7c478bd9Sstevel@tonic-gate 		 * Ensure no time overflows. Assumes underlying
5644*7c478bd9Sstevel@tonic-gate 		 * filesystem supports at least 32 bits.
5645*7c478bd9Sstevel@tonic-gate 		 * Truncate nsec to usec resolution to allow valid
5646*7c478bd9Sstevel@tonic-gate 		 * compares even if the underlying filesystem truncates.
5647*7c478bd9Sstevel@tonic-gate 		 */
5648*7c478bd9Sstevel@tonic-gate 		mtime = (timespec32_t *)&args->createhow4_u.createverf;
5649*7c478bd9Sstevel@tonic-gate 		cva.va_mtime.tv_sec = mtime->tv_sec % TIME32_MAX;
5650*7c478bd9Sstevel@tonic-gate 		cva.va_mtime.tv_nsec = (mtime->tv_nsec / 1000) * 1000;
5651*7c478bd9Sstevel@tonic-gate 		cva.va_mode = (mode_t)0;
5652*7c478bd9Sstevel@tonic-gate 		vap = &cva;
5653*7c478bd9Sstevel@tonic-gate 		break;
5654*7c478bd9Sstevel@tonic-gate 	}
5655*7c478bd9Sstevel@tonic-gate 
5656*7c478bd9Sstevel@tonic-gate 	status = create_vnode(dvp, nm, vap, args->mode, mtime,
5657*7c478bd9Sstevel@tonic-gate 						cs->cr, &vp, &created);
5658*7c478bd9Sstevel@tonic-gate 	kmem_free(nm, buflen);
5659*7c478bd9Sstevel@tonic-gate 
5660*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
5661*7c478bd9Sstevel@tonic-gate 		if (ntov_table_init)
5662*7c478bd9Sstevel@tonic-gate 			nfs4_ntov_table_free(&ntov, &sarg);
5663*7c478bd9Sstevel@tonic-gate 		*attrset = 0;
5664*7c478bd9Sstevel@tonic-gate 		return (status);
5665*7c478bd9Sstevel@tonic-gate 	}
5666*7c478bd9Sstevel@tonic-gate 
5667*7c478bd9Sstevel@tonic-gate 	trunc = (setsize && !created);
5668*7c478bd9Sstevel@tonic-gate 
5669*7c478bd9Sstevel@tonic-gate 	if (args->mode != EXCLUSIVE4) {
5670*7c478bd9Sstevel@tonic-gate 		bitmap4 createmask = args->createhow4_u.createattrs.attrmask;
5671*7c478bd9Sstevel@tonic-gate 
5672*7c478bd9Sstevel@tonic-gate 		/*
5673*7c478bd9Sstevel@tonic-gate 		 * True verification that object was created with correct
5674*7c478bd9Sstevel@tonic-gate 		 * attrs is impossible.  The attrs could have been changed
5675*7c478bd9Sstevel@tonic-gate 		 * immediately after object creation.  If attributes did
5676*7c478bd9Sstevel@tonic-gate 		 * not verify, the only recourse for the server is to
5677*7c478bd9Sstevel@tonic-gate 		 * destroy the object.  Maybe if some attrs (like gid)
5678*7c478bd9Sstevel@tonic-gate 		 * are set incorrectly, the object should be destroyed;
5679*7c478bd9Sstevel@tonic-gate 		 * however, seems bad as a default policy.  Do we really
5680*7c478bd9Sstevel@tonic-gate 		 * want to destroy an object over one of the times not
5681*7c478bd9Sstevel@tonic-gate 		 * verifying correctly?  For these reasons, the server
5682*7c478bd9Sstevel@tonic-gate 		 * currently sets bits in attrset for createattrs
5683*7c478bd9Sstevel@tonic-gate 		 * that were set; however, no verification is done.
5684*7c478bd9Sstevel@tonic-gate 		 *
5685*7c478bd9Sstevel@tonic-gate 		 * vmask_to_nmask accounts for vattr bits set on create
5686*7c478bd9Sstevel@tonic-gate 		 *	[do_rfs4_set_attrs() only sets resp bits for
5687*7c478bd9Sstevel@tonic-gate 		 *	 non-vattr/vfs bits.]
5688*7c478bd9Sstevel@tonic-gate 		 * Mask off any bits we set by default so as not to return
5689*7c478bd9Sstevel@tonic-gate 		 * more attrset bits than were requested in createattrs
5690*7c478bd9Sstevel@tonic-gate 		 */
5691*7c478bd9Sstevel@tonic-gate 		if (created) {
5692*7c478bd9Sstevel@tonic-gate 			nfs4_vmask_to_nmask(sarg.vap->va_mask, attrset);
5693*7c478bd9Sstevel@tonic-gate 			*attrset &= createmask;
5694*7c478bd9Sstevel@tonic-gate 		} else {
5695*7c478bd9Sstevel@tonic-gate 			/*
5696*7c478bd9Sstevel@tonic-gate 			 * We did not create the vnode (we tried but it
5697*7c478bd9Sstevel@tonic-gate 			 * already existed).  In this case, the only createattr
5698*7c478bd9Sstevel@tonic-gate 			 * that the spec allows the server to set is size,
5699*7c478bd9Sstevel@tonic-gate 			 * and even then, it can only be set if it is 0.
5700*7c478bd9Sstevel@tonic-gate 			 */
5701*7c478bd9Sstevel@tonic-gate 			*attrset = 0;
5702*7c478bd9Sstevel@tonic-gate 			if (trunc)
5703*7c478bd9Sstevel@tonic-gate 				*attrset = FATTR4_SIZE_MASK;
5704*7c478bd9Sstevel@tonic-gate 		}
5705*7c478bd9Sstevel@tonic-gate 	}
5706*7c478bd9Sstevel@tonic-gate 	if (ntov_table_init)
5707*7c478bd9Sstevel@tonic-gate 		nfs4_ntov_table_free(&ntov, &sarg);
5708*7c478bd9Sstevel@tonic-gate 
5709*7c478bd9Sstevel@tonic-gate 	/*
5710*7c478bd9Sstevel@tonic-gate 	 * Get the initial "after" sequence number, if it fails,
5711*7c478bd9Sstevel@tonic-gate 	 * set to zero, time to before.
5712*7c478bd9Sstevel@tonic-gate 	 */
5713*7c478bd9Sstevel@tonic-gate 	iva.va_mask = AT_CTIME|AT_SEQ;
5714*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(dvp, &iva, 0, cs->cr)) {
5715*7c478bd9Sstevel@tonic-gate 		iva.va_seq = 0;
5716*7c478bd9Sstevel@tonic-gate 		iva.va_ctime = bva.va_ctime;
5717*7c478bd9Sstevel@tonic-gate 	}
5718*7c478bd9Sstevel@tonic-gate 
5719*7c478bd9Sstevel@tonic-gate 	/*
5720*7c478bd9Sstevel@tonic-gate 	 * create_vnode attempts to create the file exclusive,
5721*7c478bd9Sstevel@tonic-gate 	 * if it already exists the VOP_CREATE will fail and
5722*7c478bd9Sstevel@tonic-gate 	 * may not increase va_seq. It is atomic if
5723*7c478bd9Sstevel@tonic-gate 	 * we haven't changed the directory, but if it has changed
5724*7c478bd9Sstevel@tonic-gate 	 * we don't know what changed it.
5725*7c478bd9Sstevel@tonic-gate 	 */
5726*7c478bd9Sstevel@tonic-gate 	if (!created) {
5727*7c478bd9Sstevel@tonic-gate 		if (bva.va_seq && iva.va_seq &&
5728*7c478bd9Sstevel@tonic-gate 			bva.va_seq == iva.va_seq)
5729*7c478bd9Sstevel@tonic-gate 			cinfo->atomic = TRUE;
5730*7c478bd9Sstevel@tonic-gate 		else
5731*7c478bd9Sstevel@tonic-gate 			cinfo->atomic = FALSE;
5732*7c478bd9Sstevel@tonic-gate 		NFS4_SET_FATTR4_CHANGE(cinfo->after, iva.va_ctime);
5733*7c478bd9Sstevel@tonic-gate 	} else {
5734*7c478bd9Sstevel@tonic-gate 		/*
5735*7c478bd9Sstevel@tonic-gate 		 * The entry was created, we need to sync the
5736*7c478bd9Sstevel@tonic-gate 		 * directory metadata.
5737*7c478bd9Sstevel@tonic-gate 		 */
5738*7c478bd9Sstevel@tonic-gate 		(void) VOP_FSYNC(dvp, 0, cs->cr);
5739*7c478bd9Sstevel@tonic-gate 
5740*7c478bd9Sstevel@tonic-gate 		/*
5741*7c478bd9Sstevel@tonic-gate 		 * Get "after" change value, if it fails, simply return the
5742*7c478bd9Sstevel@tonic-gate 		 * before value.
5743*7c478bd9Sstevel@tonic-gate 		 */
5744*7c478bd9Sstevel@tonic-gate 		ava.va_mask = AT_CTIME|AT_SEQ;
5745*7c478bd9Sstevel@tonic-gate 		if (VOP_GETATTR(dvp, &ava, 0, cs->cr)) {
5746*7c478bd9Sstevel@tonic-gate 			ava.va_ctime = bva.va_ctime;
5747*7c478bd9Sstevel@tonic-gate 			ava.va_seq = 0;
5748*7c478bd9Sstevel@tonic-gate 		}
5749*7c478bd9Sstevel@tonic-gate 
5750*7c478bd9Sstevel@tonic-gate 		NFS4_SET_FATTR4_CHANGE(cinfo->after, ava.va_ctime);
5751*7c478bd9Sstevel@tonic-gate 
5752*7c478bd9Sstevel@tonic-gate 		/*
5753*7c478bd9Sstevel@tonic-gate 		 * The cinfo->atomic = TRUE only if we have
5754*7c478bd9Sstevel@tonic-gate 		 * non-zero va_seq's, and it has incremented by exactly one
5755*7c478bd9Sstevel@tonic-gate 		 * during the create_vnode and it didn't
5756*7c478bd9Sstevel@tonic-gate 		 * change during the VOP_FSYNC.
5757*7c478bd9Sstevel@tonic-gate 		 */
5758*7c478bd9Sstevel@tonic-gate 		if (bva.va_seq && iva.va_seq && ava.va_seq &&
5759*7c478bd9Sstevel@tonic-gate 				iva.va_seq == (bva.va_seq + 1) &&
5760*7c478bd9Sstevel@tonic-gate 				iva.va_seq == ava.va_seq)
5761*7c478bd9Sstevel@tonic-gate 			cinfo->atomic = TRUE;
5762*7c478bd9Sstevel@tonic-gate 		else
5763*7c478bd9Sstevel@tonic-gate 			cinfo->atomic = FALSE;
5764*7c478bd9Sstevel@tonic-gate 	}
5765*7c478bd9Sstevel@tonic-gate 
5766*7c478bd9Sstevel@tonic-gate 	/* Check for mandatory locking and that the size gets set. */
5767*7c478bd9Sstevel@tonic-gate 	cva.va_mask = AT_MODE;
5768*7c478bd9Sstevel@tonic-gate 	if (setsize)
5769*7c478bd9Sstevel@tonic-gate 		cva.va_mask |= AT_SIZE;
5770*7c478bd9Sstevel@tonic-gate 
5771*7c478bd9Sstevel@tonic-gate 	/* Assume the worst */
5772*7c478bd9Sstevel@tonic-gate 	cs->mandlock = TRUE;
5773*7c478bd9Sstevel@tonic-gate 
5774*7c478bd9Sstevel@tonic-gate 	if (VOP_GETATTR(vp, &cva, 0, cs->cr) == 0) {
5775*7c478bd9Sstevel@tonic-gate 		cs->mandlock = MANDLOCK(cs->vp, cva.va_mode);
5776*7c478bd9Sstevel@tonic-gate 
5777*7c478bd9Sstevel@tonic-gate 		/*
5778*7c478bd9Sstevel@tonic-gate 		 * Truncate the file if necessary; this would be
5779*7c478bd9Sstevel@tonic-gate 		 * the case for create over an existing file.
5780*7c478bd9Sstevel@tonic-gate 		 */
5781*7c478bd9Sstevel@tonic-gate 
5782*7c478bd9Sstevel@tonic-gate 		if (trunc) {
5783*7c478bd9Sstevel@tonic-gate 			int in_crit = 0;
5784*7c478bd9Sstevel@tonic-gate 			rfs4_file_t *fp;
5785*7c478bd9Sstevel@tonic-gate 			bool_t create = FALSE;
5786*7c478bd9Sstevel@tonic-gate 
5787*7c478bd9Sstevel@tonic-gate 			/*
5788*7c478bd9Sstevel@tonic-gate 			 * We are writing over an existing file.
5789*7c478bd9Sstevel@tonic-gate 			 * Check to see if we need to recall a delegation.
5790*7c478bd9Sstevel@tonic-gate 			 */
5791*7c478bd9Sstevel@tonic-gate 			rfs4_hold_deleg_policy();
5792*7c478bd9Sstevel@tonic-gate 			if ((fp = rfs4_findfile(vp, NULL, &create)) != NULL) {
5793*7c478bd9Sstevel@tonic-gate 				if (rfs4_check_delegated_byfp(FWRITE, fp,
5794*7c478bd9Sstevel@tonic-gate 					(reqsize == 0), FALSE, FALSE,
5795*7c478bd9Sstevel@tonic-gate 							&clientid)) {
5796*7c478bd9Sstevel@tonic-gate 
5797*7c478bd9Sstevel@tonic-gate 					rfs4_file_rele(fp);
5798*7c478bd9Sstevel@tonic-gate 					rfs4_rele_deleg_policy();
5799*7c478bd9Sstevel@tonic-gate 					VN_RELE(vp);
5800*7c478bd9Sstevel@tonic-gate 					*attrset = 0;
5801*7c478bd9Sstevel@tonic-gate 					return (NFS4ERR_DELAY);
5802*7c478bd9Sstevel@tonic-gate 				}
5803*7c478bd9Sstevel@tonic-gate 				rfs4_file_rele(fp);
5804*7c478bd9Sstevel@tonic-gate 			}
5805*7c478bd9Sstevel@tonic-gate 			rfs4_rele_deleg_policy();
5806*7c478bd9Sstevel@tonic-gate 
5807*7c478bd9Sstevel@tonic-gate 			if (nbl_need_check(vp)) {
5808*7c478bd9Sstevel@tonic-gate 				in_crit = 1;
5809*7c478bd9Sstevel@tonic-gate 
5810*7c478bd9Sstevel@tonic-gate 				ASSERT(reqsize == 0);
5811*7c478bd9Sstevel@tonic-gate 
5812*7c478bd9Sstevel@tonic-gate 				nbl_start_crit(vp, RW_READER);
5813*7c478bd9Sstevel@tonic-gate 				if (nbl_conflict(vp, NBL_WRITE, 0,
5814*7c478bd9Sstevel@tonic-gate 						cva.va_size, 0)) {
5815*7c478bd9Sstevel@tonic-gate 					in_crit = 0;
5816*7c478bd9Sstevel@tonic-gate 					nbl_end_crit(vp);
5817*7c478bd9Sstevel@tonic-gate 					VN_RELE(vp);
5818*7c478bd9Sstevel@tonic-gate 					*attrset = 0;
5819*7c478bd9Sstevel@tonic-gate 					return (NFS4ERR_ACCESS);
5820*7c478bd9Sstevel@tonic-gate 				}
5821*7c478bd9Sstevel@tonic-gate 			}
5822*7c478bd9Sstevel@tonic-gate 			ct.cc_sysid = 0;
5823*7c478bd9Sstevel@tonic-gate 			ct.cc_pid = 0;
5824*7c478bd9Sstevel@tonic-gate 			ct.cc_caller_id = nfs4_srv_caller_id;
5825*7c478bd9Sstevel@tonic-gate 
5826*7c478bd9Sstevel@tonic-gate 			cva.va_mask = AT_SIZE;
5827*7c478bd9Sstevel@tonic-gate 			cva.va_size = reqsize;
5828*7c478bd9Sstevel@tonic-gate 			(void) VOP_SETATTR(vp, &cva, 0, cs->cr, &ct);
5829*7c478bd9Sstevel@tonic-gate 			if (in_crit)
5830*7c478bd9Sstevel@tonic-gate 				nbl_end_crit(vp);
5831*7c478bd9Sstevel@tonic-gate 		}
5832*7c478bd9Sstevel@tonic-gate 	}
5833*7c478bd9Sstevel@tonic-gate 
5834*7c478bd9Sstevel@tonic-gate 	error = makefh4(&cs->fh, vp, cs->exi);
5835*7c478bd9Sstevel@tonic-gate 
5836*7c478bd9Sstevel@tonic-gate 	/*
5837*7c478bd9Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
5838*7c478bd9Sstevel@tonic-gate 	 */
5839*7c478bd9Sstevel@tonic-gate 	(void) VOP_FSYNC(vp, FNODSYNC, cs->cr);
5840*7c478bd9Sstevel@tonic-gate 
5841*7c478bd9Sstevel@tonic-gate 	if (error) {
5842*7c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
5843*7c478bd9Sstevel@tonic-gate 		*attrset = 0;
5844*7c478bd9Sstevel@tonic-gate 		return (puterrno4(error));
5845*7c478bd9Sstevel@tonic-gate 	}
5846*7c478bd9Sstevel@tonic-gate 
5847*7c478bd9Sstevel@tonic-gate 	/* if parent dir is attrdir, set namedattr fh flag */
5848*7c478bd9Sstevel@tonic-gate 	if (dvp->v_flag & V_XATTRDIR)
5849*7c478bd9Sstevel@tonic-gate 		set_fh4_flag(&cs->fh, FH4_NAMEDATTR);
5850*7c478bd9Sstevel@tonic-gate 
5851*7c478bd9Sstevel@tonic-gate 	if (cs->vp)
5852*7c478bd9Sstevel@tonic-gate 		VN_RELE(cs->vp);
5853*7c478bd9Sstevel@tonic-gate 
5854*7c478bd9Sstevel@tonic-gate 	cs->vp = vp;
5855*7c478bd9Sstevel@tonic-gate 
5856*7c478bd9Sstevel@tonic-gate 	/*
5857*7c478bd9Sstevel@tonic-gate 	 * if we did not create the file, we will need to check
5858*7c478bd9Sstevel@tonic-gate 	 * the access bits on the file
5859*7c478bd9Sstevel@tonic-gate 	 */
5860*7c478bd9Sstevel@tonic-gate 
5861*7c478bd9Sstevel@tonic-gate 	if (!created) {
5862*7c478bd9Sstevel@tonic-gate 		if (setsize)
5863*7c478bd9Sstevel@tonic-gate 			args->share_access |= OPEN4_SHARE_ACCESS_WRITE;
5864*7c478bd9Sstevel@tonic-gate 		status = check_open_access(args->share_access, cs, req);
5865*7c478bd9Sstevel@tonic-gate 		if (status != NFS4_OK)
5866*7c478bd9Sstevel@tonic-gate 			*attrset = 0;
5867*7c478bd9Sstevel@tonic-gate 	}
5868*7c478bd9Sstevel@tonic-gate 	return (status);
5869*7c478bd9Sstevel@tonic-gate }
5870*7c478bd9Sstevel@tonic-gate 
5871*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5872*7c478bd9Sstevel@tonic-gate static void
5873*7c478bd9Sstevel@tonic-gate rfs4_do_open(struct compound_state *cs, struct svc_req *req,
5874*7c478bd9Sstevel@tonic-gate 		rfs4_openowner_t *oo, delegreq_t deleg,
5875*7c478bd9Sstevel@tonic-gate 		uint32_t access, uint32_t deny,
5876*7c478bd9Sstevel@tonic-gate 		OPEN4res *resp)
5877*7c478bd9Sstevel@tonic-gate {
5878*7c478bd9Sstevel@tonic-gate 	/* XXX Currently not using req  */
5879*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *state;
5880*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *file;
5881*7c478bd9Sstevel@tonic-gate 	bool_t screate = TRUE;
5882*7c478bd9Sstevel@tonic-gate 	bool_t fcreate = TRUE;
5883*7c478bd9Sstevel@tonic-gate 	uint32_t amodes;
5884*7c478bd9Sstevel@tonic-gate 	uint32_t dmodes;
5885*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
5886*7c478bd9Sstevel@tonic-gate 	struct shrlock shr;
5887*7c478bd9Sstevel@tonic-gate 	struct shr_locowner shr_loco;
5888*7c478bd9Sstevel@tonic-gate 	sysid_t sysid;
5889*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
5890*7c478bd9Sstevel@tonic-gate 	int fflags = 0;
5891*7c478bd9Sstevel@tonic-gate 	int recall = 0;
5892*7c478bd9Sstevel@tonic-gate 	int err;
5893*7c478bd9Sstevel@tonic-gate 
5894*7c478bd9Sstevel@tonic-gate 	/* get the file struct and hold a lock on it during initial open */
5895*7c478bd9Sstevel@tonic-gate 	file = rfs4_findfile_withlock(cs->vp, &cs->fh, &fcreate);
5896*7c478bd9Sstevel@tonic-gate 	if (file == NULL) {
5897*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
5898*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_do_open: can't find file"));
5899*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SERVERFAULT;
5900*7c478bd9Sstevel@tonic-gate 		return;
5901*7c478bd9Sstevel@tonic-gate 	}
5902*7c478bd9Sstevel@tonic-gate 
5903*7c478bd9Sstevel@tonic-gate 	state = rfs4_findstate_by_owner_file(oo, file, &screate);
5904*7c478bd9Sstevel@tonic-gate 	if (state == NULL) {
5905*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
5906*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_do_open: can't find state"));
5907*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_RESOURCE;
5908*7c478bd9Sstevel@tonic-gate 		/* No need to keep any reference */
5909*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele_withunlock(file);
5910*7c478bd9Sstevel@tonic-gate 		return;
5911*7c478bd9Sstevel@tonic-gate 	}
5912*7c478bd9Sstevel@tonic-gate 
5913*7c478bd9Sstevel@tonic-gate 	/*
5914*7c478bd9Sstevel@tonic-gate 	 * Check for conflicts in deny and access before checking for
5915*7c478bd9Sstevel@tonic-gate 	 * conflicts in delegation.  We don't want to recall a
5916*7c478bd9Sstevel@tonic-gate 	 * delegation based on an open that will eventually fail based
5917*7c478bd9Sstevel@tonic-gate 	 * on shares modes.
5918*7c478bd9Sstevel@tonic-gate 	 */
5919*7c478bd9Sstevel@tonic-gate 
5920*7c478bd9Sstevel@tonic-gate 	shr.s_access = (short)access;
5921*7c478bd9Sstevel@tonic-gate 	shr.s_deny = (short)deny;
5922*7c478bd9Sstevel@tonic-gate 	shr.s_pid = rfs4_dbe_getid(oo->dbe);
5923*7c478bd9Sstevel@tonic-gate 
5924*7c478bd9Sstevel@tonic-gate 	if ((status = rfs4_client_sysid(oo->client, &sysid)) != NFS4_OK) {
5925*7c478bd9Sstevel@tonic-gate 		resp->status = status;
5926*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(file);
5927*7c478bd9Sstevel@tonic-gate 		/* Not a fully formed open; "close" it */
5928*7c478bd9Sstevel@tonic-gate 		if (screate == TRUE)
5929*7c478bd9Sstevel@tonic-gate 			rfs4_state_close(state, FALSE, FALSE, cs->cr);
5930*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(state);
5931*7c478bd9Sstevel@tonic-gate 		return;
5932*7c478bd9Sstevel@tonic-gate 	}
5933*7c478bd9Sstevel@tonic-gate 	shr.s_sysid = sysid;
5934*7c478bd9Sstevel@tonic-gate 	shr_loco.sl_pid = shr.s_pid;
5935*7c478bd9Sstevel@tonic-gate 	shr_loco.sl_id = shr.s_sysid;
5936*7c478bd9Sstevel@tonic-gate 	shr.s_owner = (caddr_t)&shr_loco;
5937*7c478bd9Sstevel@tonic-gate 	shr.s_own_len = sizeof (shr_loco);
5938*7c478bd9Sstevel@tonic-gate 
5939*7c478bd9Sstevel@tonic-gate 	fflags = 0;
5940*7c478bd9Sstevel@tonic-gate 	if (access & OPEN4_SHARE_ACCESS_READ)
5941*7c478bd9Sstevel@tonic-gate 		fflags |= FREAD;
5942*7c478bd9Sstevel@tonic-gate 	if (access & OPEN4_SHARE_ACCESS_WRITE)
5943*7c478bd9Sstevel@tonic-gate 		fflags |= FWRITE;
5944*7c478bd9Sstevel@tonic-gate 
5945*7c478bd9Sstevel@tonic-gate 	if ((err = vop_shrlock(cs->vp, F_SHARE, &shr, fflags)) != 0) {
5946*7c478bd9Sstevel@tonic-gate 
5947*7c478bd9Sstevel@tonic-gate 		resp->status = err == EAGAIN ?
5948*7c478bd9Sstevel@tonic-gate 			NFS4ERR_SHARE_DENIED : puterrno4(err);
5949*7c478bd9Sstevel@tonic-gate 
5950*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(file);
5951*7c478bd9Sstevel@tonic-gate 		/* Not a fully formed open; "close" it */
5952*7c478bd9Sstevel@tonic-gate 		if (screate == TRUE)
5953*7c478bd9Sstevel@tonic-gate 			rfs4_state_close(state, FALSE, FALSE, cs->cr);
5954*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(state);
5955*7c478bd9Sstevel@tonic-gate 		return;
5956*7c478bd9Sstevel@tonic-gate 	}
5957*7c478bd9Sstevel@tonic-gate 
5958*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(state->dbe);
5959*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(file->dbe);
5960*7c478bd9Sstevel@tonic-gate 
5961*7c478bd9Sstevel@tonic-gate 	/*
5962*7c478bd9Sstevel@tonic-gate 	 * Calculate the new deny and access mode that this open is adding to
5963*7c478bd9Sstevel@tonic-gate 	 * the file for this open owner;
5964*7c478bd9Sstevel@tonic-gate 	 */
5965*7c478bd9Sstevel@tonic-gate 	dmodes = (deny & ~state->share_deny);
5966*7c478bd9Sstevel@tonic-gate 	amodes = (access & ~state->share_access);
5967*7c478bd9Sstevel@tonic-gate 
5968*7c478bd9Sstevel@tonic-gate 	/*
5969*7c478bd9Sstevel@tonic-gate 	 * Check to see if this file is delegated and if so, if a
5970*7c478bd9Sstevel@tonic-gate 	 * recall needs to be done.
5971*7c478bd9Sstevel@tonic-gate 	 */
5972*7c478bd9Sstevel@tonic-gate 	if (rfs4_check_recall(state, access)) {
5973*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(file->dbe);
5974*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(state->dbe);
5975*7c478bd9Sstevel@tonic-gate 		rfs4_recall_deleg(file, FALSE, state->owner->client);
5976*7c478bd9Sstevel@tonic-gate 		delay(NFS4_DELEGATION_CONFLICT_DELAY);
5977*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(state->dbe);
5978*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(file->dbe);
5979*7c478bd9Sstevel@tonic-gate 		/* Let's see if the delegation was returned */
5980*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_recall(state, access)) {
5981*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_unlock(file->dbe);
5982*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_unlock(state->dbe);
5983*7c478bd9Sstevel@tonic-gate 			rfs4_file_rele(file);
5984*7c478bd9Sstevel@tonic-gate 			rfs4_update_lease(state->owner->client);
5985*7c478bd9Sstevel@tonic-gate 			/* recalculate flags to match what was added */
5986*7c478bd9Sstevel@tonic-gate 			fflags = 0;
5987*7c478bd9Sstevel@tonic-gate 			if (amodes & OPEN4_SHARE_ACCESS_READ)
5988*7c478bd9Sstevel@tonic-gate 				fflags |= FREAD;
5989*7c478bd9Sstevel@tonic-gate 			if (amodes & OPEN4_SHARE_ACCESS_WRITE)
5990*7c478bd9Sstevel@tonic-gate 				fflags |= FWRITE;
5991*7c478bd9Sstevel@tonic-gate 			(void) vop_shrlock(cs->vp, F_UNSHARE, &shr, fflags);
5992*7c478bd9Sstevel@tonic-gate 			/* Not a fully formed open; "close" it */
5993*7c478bd9Sstevel@tonic-gate 			if (screate == TRUE)
5994*7c478bd9Sstevel@tonic-gate 				rfs4_state_close(state, FALSE, FALSE, cs->cr);
5995*7c478bd9Sstevel@tonic-gate 			rfs4_state_rele(state);
5996*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_DELAY;
5997*7c478bd9Sstevel@tonic-gate 			return;
5998*7c478bd9Sstevel@tonic-gate 		}
5999*7c478bd9Sstevel@tonic-gate 	}
6000*7c478bd9Sstevel@tonic-gate 
6001*7c478bd9Sstevel@tonic-gate 	if (dmodes & OPEN4_SHARE_DENY_READ)
6002*7c478bd9Sstevel@tonic-gate 		file->deny_read++;
6003*7c478bd9Sstevel@tonic-gate 	if (dmodes & OPEN4_SHARE_DENY_WRITE)
6004*7c478bd9Sstevel@tonic-gate 		file->deny_write++;
6005*7c478bd9Sstevel@tonic-gate 	file->share_deny |= deny;
6006*7c478bd9Sstevel@tonic-gate 	state->share_deny |= deny;
6007*7c478bd9Sstevel@tonic-gate 
6008*7c478bd9Sstevel@tonic-gate 	if (amodes & OPEN4_SHARE_ACCESS_READ)
6009*7c478bd9Sstevel@tonic-gate 		file->access_read++;
6010*7c478bd9Sstevel@tonic-gate 	if (amodes & OPEN4_SHARE_ACCESS_WRITE)
6011*7c478bd9Sstevel@tonic-gate 		file->access_write++;
6012*7c478bd9Sstevel@tonic-gate 	file->share_access |= access;
6013*7c478bd9Sstevel@tonic-gate 	state->share_access |= access;
6014*7c478bd9Sstevel@tonic-gate 
6015*7c478bd9Sstevel@tonic-gate 	/*
6016*7c478bd9Sstevel@tonic-gate 	 * Check for delegation here. if the deleg argument is not
6017*7c478bd9Sstevel@tonic-gate 	 * DELEG_ANY, then this is a reclaim from a client and
6018*7c478bd9Sstevel@tonic-gate 	 * we must honor the delegation requested. If necessary we can
6019*7c478bd9Sstevel@tonic-gate 	 * set the recall flag.
6020*7c478bd9Sstevel@tonic-gate 	 */
6021*7c478bd9Sstevel@tonic-gate 
6022*7c478bd9Sstevel@tonic-gate 	dsp = rfs4_grant_delegation(deleg, state, &recall);
6023*7c478bd9Sstevel@tonic-gate 
6024*7c478bd9Sstevel@tonic-gate 	cs->deleg = (file->dinfo->dtype == OPEN_DELEGATE_WRITE);
6025*7c478bd9Sstevel@tonic-gate 
6026*7c478bd9Sstevel@tonic-gate 	next_stateid(&state->stateid);
6027*7c478bd9Sstevel@tonic-gate 
6028*7c478bd9Sstevel@tonic-gate 	resp->stateid = state->stateid.stateid;
6029*7c478bd9Sstevel@tonic-gate 
6030*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(file->dbe);
6031*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(state->dbe);
6032*7c478bd9Sstevel@tonic-gate 
6033*7c478bd9Sstevel@tonic-gate 	if (dsp) {
6034*7c478bd9Sstevel@tonic-gate 		rfs4_set_deleg_response(dsp, &resp->delegation, NULL, recall);
6035*7c478bd9Sstevel@tonic-gate 		rfs4_deleg_state_rele(dsp);
6036*7c478bd9Sstevel@tonic-gate 	}
6037*7c478bd9Sstevel@tonic-gate 
6038*7c478bd9Sstevel@tonic-gate 	rfs4_file_rele(file);
6039*7c478bd9Sstevel@tonic-gate 	rfs4_state_rele(state);
6040*7c478bd9Sstevel@tonic-gate 
6041*7c478bd9Sstevel@tonic-gate 	resp->status = NFS4_OK;
6042*7c478bd9Sstevel@tonic-gate }
6043*7c478bd9Sstevel@tonic-gate 
6044*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6045*7c478bd9Sstevel@tonic-gate static void
6046*7c478bd9Sstevel@tonic-gate rfs4_do_opennull(struct compound_state *cs, struct svc_req *req,
6047*7c478bd9Sstevel@tonic-gate 		OPEN4args *args, rfs4_openowner_t *oo, OPEN4res *resp)
6048*7c478bd9Sstevel@tonic-gate {
6049*7c478bd9Sstevel@tonic-gate 	change_info4 *cinfo = &resp->cinfo;
6050*7c478bd9Sstevel@tonic-gate 	bitmap4 *attrset = &resp->attrset;
6051*7c478bd9Sstevel@tonic-gate 
6052*7c478bd9Sstevel@tonic-gate 	if (args->opentype == OPEN4_NOCREATE)
6053*7c478bd9Sstevel@tonic-gate 		resp->status = rfs4_lookupfile(&args->open_claim4_u.file,
6054*7c478bd9Sstevel@tonic-gate 					req, cs, args->share_access, cinfo);
6055*7c478bd9Sstevel@tonic-gate 	else {
6056*7c478bd9Sstevel@tonic-gate 		/* inhibit delegation grants during exclusive create */
6057*7c478bd9Sstevel@tonic-gate 
6058*7c478bd9Sstevel@tonic-gate 		if (args->mode == EXCLUSIVE4)
6059*7c478bd9Sstevel@tonic-gate 			rfs4_disable_delegation();
6060*7c478bd9Sstevel@tonic-gate 
6061*7c478bd9Sstevel@tonic-gate 		resp->status = rfs4_createfile(args, req, cs, cinfo, attrset,
6062*7c478bd9Sstevel@tonic-gate 					oo->client->clientid);
6063*7c478bd9Sstevel@tonic-gate 	}
6064*7c478bd9Sstevel@tonic-gate 
6065*7c478bd9Sstevel@tonic-gate 	if (resp->status == NFS4_OK) {
6066*7c478bd9Sstevel@tonic-gate 
6067*7c478bd9Sstevel@tonic-gate 		/* cs->vp cs->fh now reference the desired file */
6068*7c478bd9Sstevel@tonic-gate 
6069*7c478bd9Sstevel@tonic-gate 		rfs4_do_open(cs, req, oo, DELEG_ANY, args->share_access,
6070*7c478bd9Sstevel@tonic-gate 						args->share_deny, resp);
6071*7c478bd9Sstevel@tonic-gate 
6072*7c478bd9Sstevel@tonic-gate 		/*
6073*7c478bd9Sstevel@tonic-gate 		 * If rfs4_createfile set attrset, we must
6074*7c478bd9Sstevel@tonic-gate 		 * clear this attrset before the response is copied.
6075*7c478bd9Sstevel@tonic-gate 		 */
6076*7c478bd9Sstevel@tonic-gate 		if (resp->status != NFS4_OK && resp->attrset) {
6077*7c478bd9Sstevel@tonic-gate 			resp->attrset = 0;
6078*7c478bd9Sstevel@tonic-gate 		}
6079*7c478bd9Sstevel@tonic-gate 	}
6080*7c478bd9Sstevel@tonic-gate 	else
6081*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status;
6082*7c478bd9Sstevel@tonic-gate 
6083*7c478bd9Sstevel@tonic-gate 	if (args->mode == EXCLUSIVE4)
6084*7c478bd9Sstevel@tonic-gate 		rfs4_enable_delegation();
6085*7c478bd9Sstevel@tonic-gate }
6086*7c478bd9Sstevel@tonic-gate 
6087*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6088*7c478bd9Sstevel@tonic-gate static void
6089*7c478bd9Sstevel@tonic-gate rfs4_do_openprev(struct compound_state *cs, struct svc_req *req,
6090*7c478bd9Sstevel@tonic-gate 		OPEN4args *args, rfs4_openowner_t *oo, OPEN4res *resp)
6091*7c478bd9Sstevel@tonic-gate {
6092*7c478bd9Sstevel@tonic-gate 	change_info4 *cinfo = &resp->cinfo;
6093*7c478bd9Sstevel@tonic-gate 	vattr_t va;
6094*7c478bd9Sstevel@tonic-gate 	vtype_t v_type = cs->vp->v_type;
6095*7c478bd9Sstevel@tonic-gate 	int error = 0;
6096*7c478bd9Sstevel@tonic-gate 
6097*7c478bd9Sstevel@tonic-gate 	/* Verify that we have a regular file */
6098*7c478bd9Sstevel@tonic-gate 	if (v_type != VREG) {
6099*7c478bd9Sstevel@tonic-gate 		if (v_type == VDIR)
6100*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_ISDIR;
6101*7c478bd9Sstevel@tonic-gate 		else if (v_type == VLNK)
6102*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_SYMLINK;
6103*7c478bd9Sstevel@tonic-gate 		else
6104*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_INVAL;
6105*7c478bd9Sstevel@tonic-gate 		return;
6106*7c478bd9Sstevel@tonic-gate 	}
6107*7c478bd9Sstevel@tonic-gate 
6108*7c478bd9Sstevel@tonic-gate 	va.va_mask = AT_MODE|AT_UID;
6109*7c478bd9Sstevel@tonic-gate 	error = VOP_GETATTR(cs->vp, &va, 0, cs->cr);
6110*7c478bd9Sstevel@tonic-gate 	if (error) {
6111*7c478bd9Sstevel@tonic-gate 		resp->status = puterrno4(error);
6112*7c478bd9Sstevel@tonic-gate 		return;
6113*7c478bd9Sstevel@tonic-gate 	}
6114*7c478bd9Sstevel@tonic-gate 
6115*7c478bd9Sstevel@tonic-gate 	cs->mandlock = MANDLOCK(cs->vp, va.va_mode);
6116*7c478bd9Sstevel@tonic-gate 
6117*7c478bd9Sstevel@tonic-gate 	/*
6118*7c478bd9Sstevel@tonic-gate 	 * Check if we have access to the file, Note the the file
6119*7c478bd9Sstevel@tonic-gate 	 * could have originally been open UNCHECKED or GUARDED
6120*7c478bd9Sstevel@tonic-gate 	 * with mode bits that will now fail, but there is nothing
6121*7c478bd9Sstevel@tonic-gate 	 * we can really do about that except in the case that the
6122*7c478bd9Sstevel@tonic-gate 	 * owner of the file is the one requesting the open.
6123*7c478bd9Sstevel@tonic-gate 	 */
6124*7c478bd9Sstevel@tonic-gate 	if (crgetuid(cs->cr) != va.va_uid) {
6125*7c478bd9Sstevel@tonic-gate 		resp->status = check_open_access(args->share_access, cs, req);
6126*7c478bd9Sstevel@tonic-gate 		if (resp->status != NFS4_OK) {
6127*7c478bd9Sstevel@tonic-gate 			return;
6128*7c478bd9Sstevel@tonic-gate 		}
6129*7c478bd9Sstevel@tonic-gate 	}
6130*7c478bd9Sstevel@tonic-gate 
6131*7c478bd9Sstevel@tonic-gate 	/*
6132*7c478bd9Sstevel@tonic-gate 	 * cinfo on a CLAIM_PREVIOUS is undefined, initialize to zero
6133*7c478bd9Sstevel@tonic-gate 	 */
6134*7c478bd9Sstevel@tonic-gate 	cinfo->before = 0;
6135*7c478bd9Sstevel@tonic-gate 	cinfo->after = 0;
6136*7c478bd9Sstevel@tonic-gate 	cinfo->atomic = FALSE;
6137*7c478bd9Sstevel@tonic-gate 
6138*7c478bd9Sstevel@tonic-gate 	rfs4_do_open(cs, req, oo,
6139*7c478bd9Sstevel@tonic-gate 		NFS4_DELEG4TYPE2REQTYPE(args->open_claim4_u.delegate_type),
6140*7c478bd9Sstevel@tonic-gate 		args->share_access, args->share_deny, resp);
6141*7c478bd9Sstevel@tonic-gate }
6142*7c478bd9Sstevel@tonic-gate 
6143*7c478bd9Sstevel@tonic-gate static void
6144*7c478bd9Sstevel@tonic-gate rfs4_do_opendelcur(struct compound_state *cs, struct svc_req *req,
6145*7c478bd9Sstevel@tonic-gate 		OPEN4args *args, rfs4_openowner_t *oo, OPEN4res *resp)
6146*7c478bd9Sstevel@tonic-gate {
6147*7c478bd9Sstevel@tonic-gate 	int error;
6148*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
6149*7c478bd9Sstevel@tonic-gate 	stateid4 stateid =
6150*7c478bd9Sstevel@tonic-gate 			args->open_claim4_u.delegate_cur_info.delegate_stateid;
6151*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
6152*7c478bd9Sstevel@tonic-gate 
6153*7c478bd9Sstevel@tonic-gate 	/*
6154*7c478bd9Sstevel@tonic-gate 	 * Find the state info from the stateid and confirm that the
6155*7c478bd9Sstevel@tonic-gate 	 * file is delegated.  If the state openowner is the same as
6156*7c478bd9Sstevel@tonic-gate 	 * the supplied openowner we're done. If not, get the file
6157*7c478bd9Sstevel@tonic-gate 	 * info from the found state info. Use that file info to
6158*7c478bd9Sstevel@tonic-gate 	 * create the state for this lock owner. Note solaris doen't
6159*7c478bd9Sstevel@tonic-gate 	 * really need the pathname to find the file. We may want to
6160*7c478bd9Sstevel@tonic-gate 	 * lookup the pathname and make sure that the vp exist and
6161*7c478bd9Sstevel@tonic-gate 	 * matches the vp in the file structure. However it is
6162*7c478bd9Sstevel@tonic-gate 	 * possible that the pathname nolonger exists (local process
6163*7c478bd9Sstevel@tonic-gate 	 * unlinks the file), so this may not be that useful.
6164*7c478bd9Sstevel@tonic-gate 	 */
6165*7c478bd9Sstevel@tonic-gate 
6166*7c478bd9Sstevel@tonic-gate 	status = rfs4_get_deleg_state(&stateid, &dsp);
6167*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
6168*7c478bd9Sstevel@tonic-gate 		resp->status = status;
6169*7c478bd9Sstevel@tonic-gate 		return;
6170*7c478bd9Sstevel@tonic-gate 	}
6171*7c478bd9Sstevel@tonic-gate 
6172*7c478bd9Sstevel@tonic-gate 	ASSERT(dsp->finfo->dinfo->dtype != OPEN_DELEGATE_NONE);
6173*7c478bd9Sstevel@tonic-gate 
6174*7c478bd9Sstevel@tonic-gate 	/*
6175*7c478bd9Sstevel@tonic-gate 	 * New lock owner, create state. Since this was probably called
6176*7c478bd9Sstevel@tonic-gate 	 * in response to a CB_RECALL we set deleg to DELEG_NONE
6177*7c478bd9Sstevel@tonic-gate 	 */
6178*7c478bd9Sstevel@tonic-gate 
6179*7c478bd9Sstevel@tonic-gate 	ASSERT(cs->vp != NULL);
6180*7c478bd9Sstevel@tonic-gate 	VN_RELE(cs->vp);
6181*7c478bd9Sstevel@tonic-gate 	VN_HOLD(dsp->finfo->vp);
6182*7c478bd9Sstevel@tonic-gate 	cs->vp = dsp->finfo->vp;
6183*7c478bd9Sstevel@tonic-gate 
6184*7c478bd9Sstevel@tonic-gate 	if (error = makefh4(&cs->fh, cs->vp, cs->exi)) {
6185*7c478bd9Sstevel@tonic-gate 		rfs4_deleg_state_rele(dsp);
6186*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = puterrno4(error);
6187*7c478bd9Sstevel@tonic-gate 		return;
6188*7c478bd9Sstevel@tonic-gate 	}
6189*7c478bd9Sstevel@tonic-gate 
6190*7c478bd9Sstevel@tonic-gate 	/* Mark progress for delegation returns */
6191*7c478bd9Sstevel@tonic-gate 	dsp->finfo->dinfo->time_lastwrite = gethrestime_sec();
6192*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_rele(dsp);
6193*7c478bd9Sstevel@tonic-gate 	rfs4_do_open(cs, req, oo, DELEG_NONE,
6194*7c478bd9Sstevel@tonic-gate 				args->share_access, args->share_deny, resp);
6195*7c478bd9Sstevel@tonic-gate }
6196*7c478bd9Sstevel@tonic-gate 
6197*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6198*7c478bd9Sstevel@tonic-gate static void
6199*7c478bd9Sstevel@tonic-gate rfs4_do_opendelprev(struct compound_state *cs, struct svc_req *req,
6200*7c478bd9Sstevel@tonic-gate 			OPEN4args *args, rfs4_openowner_t *oo, OPEN4res *resp)
6201*7c478bd9Sstevel@tonic-gate {
6202*7c478bd9Sstevel@tonic-gate 	/*
6203*7c478bd9Sstevel@tonic-gate 	 * Lookup the pathname, it must already exist since this file
6204*7c478bd9Sstevel@tonic-gate 	 * was delegated.
6205*7c478bd9Sstevel@tonic-gate 	 *
6206*7c478bd9Sstevel@tonic-gate 	 * Find the file and state info for this vp and open owner pair.
6207*7c478bd9Sstevel@tonic-gate 	 *	check that they are in fact delegated.
6208*7c478bd9Sstevel@tonic-gate 	 *	check that the state access and deny modes are the same.
6209*7c478bd9Sstevel@tonic-gate 	 *
6210*7c478bd9Sstevel@tonic-gate 	 * Return the delgation possibly seting the recall flag.
6211*7c478bd9Sstevel@tonic-gate 	 */
6212*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *file;
6213*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *state;
6214*7c478bd9Sstevel@tonic-gate 	bool_t create = FALSE;
6215*7c478bd9Sstevel@tonic-gate 	bool_t dcreate = FALSE;
6216*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_t *dsp;
6217*7c478bd9Sstevel@tonic-gate 	nfsace4 *ace;
6218*7c478bd9Sstevel@tonic-gate 
6219*7c478bd9Sstevel@tonic-gate 
6220*7c478bd9Sstevel@tonic-gate 	/* Note we ignore oflags */
6221*7c478bd9Sstevel@tonic-gate 	resp->status = rfs4_lookupfile(&args->open_claim4_u.file_delegate_prev,
6222*7c478bd9Sstevel@tonic-gate 				req, cs, args->share_access, &resp->cinfo);
6223*7c478bd9Sstevel@tonic-gate 
6224*7c478bd9Sstevel@tonic-gate 	if (resp->status != NFS4_OK) {
6225*7c478bd9Sstevel@tonic-gate 		return;
6226*7c478bd9Sstevel@tonic-gate 	}
6227*7c478bd9Sstevel@tonic-gate 
6228*7c478bd9Sstevel@tonic-gate 	/* get the file struct and hold a lock on it during initial open */
6229*7c478bd9Sstevel@tonic-gate 	file = rfs4_findfile_withlock(cs->vp, NULL, &create);
6230*7c478bd9Sstevel@tonic-gate 	if (file == NULL) {
6231*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
6232*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_do_opendelprev: can't find file"));
6233*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SERVERFAULT;
6234*7c478bd9Sstevel@tonic-gate 		return;
6235*7c478bd9Sstevel@tonic-gate 	}
6236*7c478bd9Sstevel@tonic-gate 
6237*7c478bd9Sstevel@tonic-gate 	state = rfs4_findstate_by_owner_file(oo, file, &create);
6238*7c478bd9Sstevel@tonic-gate 	if (state == NULL) {
6239*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
6240*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_do_opendelprev: can't find state"));
6241*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SERVERFAULT;
6242*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele_withunlock(file);
6243*7c478bd9Sstevel@tonic-gate 		return;
6244*7c478bd9Sstevel@tonic-gate 	}
6245*7c478bd9Sstevel@tonic-gate 
6246*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(state->dbe);
6247*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(file->dbe);
6248*7c478bd9Sstevel@tonic-gate 	if (args->share_access != state->share_access ||
6249*7c478bd9Sstevel@tonic-gate 			args->share_deny != state->share_deny ||
6250*7c478bd9Sstevel@tonic-gate 			state->finfo->dinfo->dtype == OPEN_DELEGATE_NONE) {
6251*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
6252*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "rfs4_do_opendelprev: state mixup"));
6253*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(file->dbe);
6254*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(state->dbe);
6255*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(file);
6256*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(state);
6257*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SERVERFAULT;
6258*7c478bd9Sstevel@tonic-gate 		return;
6259*7c478bd9Sstevel@tonic-gate 	}
6260*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(file->dbe);
6261*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(state->dbe);
6262*7c478bd9Sstevel@tonic-gate 
6263*7c478bd9Sstevel@tonic-gate 	dsp = rfs4_finddeleg(state, &dcreate);
6264*7c478bd9Sstevel@tonic-gate 	if (dsp == NULL) {
6265*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(state);
6266*7c478bd9Sstevel@tonic-gate 		rfs4_file_rele(file);
6267*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SERVERFAULT;
6268*7c478bd9Sstevel@tonic-gate 		return;
6269*7c478bd9Sstevel@tonic-gate 	}
6270*7c478bd9Sstevel@tonic-gate 
6271*7c478bd9Sstevel@tonic-gate 	next_stateid(&state->stateid);
6272*7c478bd9Sstevel@tonic-gate 
6273*7c478bd9Sstevel@tonic-gate 	resp->stateid = state->stateid.stateid;
6274*7c478bd9Sstevel@tonic-gate 
6275*7c478bd9Sstevel@tonic-gate 	resp->delegation.delegation_type = dsp->dtype;
6276*7c478bd9Sstevel@tonic-gate 
6277*7c478bd9Sstevel@tonic-gate 	if (dsp->dtype == OPEN_DELEGATE_READ) {
6278*7c478bd9Sstevel@tonic-gate 		open_read_delegation4 *rv =
6279*7c478bd9Sstevel@tonic-gate 			&resp->delegation.open_delegation4_u.read;
6280*7c478bd9Sstevel@tonic-gate 
6281*7c478bd9Sstevel@tonic-gate 		rv->stateid = dsp->delegid.stateid;
6282*7c478bd9Sstevel@tonic-gate 		rv->recall = FALSE; /* no policy in place to set to TRUE */
6283*7c478bd9Sstevel@tonic-gate 		ace = &rv->permissions;
6284*7c478bd9Sstevel@tonic-gate 	} else {
6285*7c478bd9Sstevel@tonic-gate 		open_write_delegation4 *rv =
6286*7c478bd9Sstevel@tonic-gate 			&resp->delegation.open_delegation4_u.write;
6287*7c478bd9Sstevel@tonic-gate 
6288*7c478bd9Sstevel@tonic-gate 		rv->stateid = dsp->delegid.stateid;
6289*7c478bd9Sstevel@tonic-gate 		rv->recall = FALSE;  /* no policy in place to set to TRUE */
6290*7c478bd9Sstevel@tonic-gate 		ace = &rv->permissions;
6291*7c478bd9Sstevel@tonic-gate 		rv->space_limit.limitby = NFS_LIMIT_SIZE;
6292*7c478bd9Sstevel@tonic-gate 		rv->space_limit.nfs_space_limit4_u.filesize = UINT64_MAX;
6293*7c478bd9Sstevel@tonic-gate 	}
6294*7c478bd9Sstevel@tonic-gate 
6295*7c478bd9Sstevel@tonic-gate 	/* XXX For now */
6296*7c478bd9Sstevel@tonic-gate 	ace->type = ACE4_ACCESS_ALLOWED_ACE_TYPE;
6297*7c478bd9Sstevel@tonic-gate 	ace->flag = 0;
6298*7c478bd9Sstevel@tonic-gate 	ace->access_mask = 0;
6299*7c478bd9Sstevel@tonic-gate 	ace->who.utf8string_len = 0;
6300*7c478bd9Sstevel@tonic-gate 	ace->who.utf8string_val = 0;
6301*7c478bd9Sstevel@tonic-gate 
6302*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_state_rele(dsp);
6303*7c478bd9Sstevel@tonic-gate 	rfs4_state_rele(state);
6304*7c478bd9Sstevel@tonic-gate 	rfs4_file_rele(file);
6305*7c478bd9Sstevel@tonic-gate }
6306*7c478bd9Sstevel@tonic-gate 
6307*7c478bd9Sstevel@tonic-gate typedef enum {
6308*7c478bd9Sstevel@tonic-gate 	NFS4_CHKSEQ_OKAY = 0,
6309*7c478bd9Sstevel@tonic-gate 	NFS4_CHKSEQ_REPLAY = 1,
6310*7c478bd9Sstevel@tonic-gate 	NFS4_CHKSEQ_BAD = 2
6311*7c478bd9Sstevel@tonic-gate } rfs4_chkseq_t;
6312*7c478bd9Sstevel@tonic-gate 
6313*7c478bd9Sstevel@tonic-gate /*
6314*7c478bd9Sstevel@tonic-gate  * Generic function for sequence number checks.
6315*7c478bd9Sstevel@tonic-gate  */
6316*7c478bd9Sstevel@tonic-gate static rfs4_chkseq_t
6317*7c478bd9Sstevel@tonic-gate rfs4_check_seqid(seqid4 seqid, nfs_resop4 *lastop,
6318*7c478bd9Sstevel@tonic-gate 		seqid4 rqst_seq, nfs_resop4 *resop, bool_t copyres)
6319*7c478bd9Sstevel@tonic-gate {
6320*7c478bd9Sstevel@tonic-gate 	/* Same sequence ids and matching operations? */
6321*7c478bd9Sstevel@tonic-gate 	if (seqid == rqst_seq && resop->resop == lastop->resop) {
6322*7c478bd9Sstevel@tonic-gate 		if (copyres == TRUE) {
6323*7c478bd9Sstevel@tonic-gate 			rfs4_free_reply(resop);
6324*7c478bd9Sstevel@tonic-gate 			rfs4_copy_reply(resop, lastop);
6325*7c478bd9Sstevel@tonic-gate 		}
6326*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug, (CE_NOTE,
6327*7c478bd9Sstevel@tonic-gate 			"Replayed SEQID %d\n", seqid));
6328*7c478bd9Sstevel@tonic-gate 		return (NFS4_CHKSEQ_REPLAY);
6329*7c478bd9Sstevel@tonic-gate 	}
6330*7c478bd9Sstevel@tonic-gate 
6331*7c478bd9Sstevel@tonic-gate 	/* If the incoming sequence is not the next expected then it is bad */
6332*7c478bd9Sstevel@tonic-gate 	if (rqst_seq != seqid + 1) {
6333*7c478bd9Sstevel@tonic-gate 		if (rqst_seq == seqid) {
6334*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug,
6335*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "BAD SEQID: Replayed sequence id "
6336*7c478bd9Sstevel@tonic-gate 				"but last op was %d current op is %d\n",
6337*7c478bd9Sstevel@tonic-gate 				lastop->resop, resop->resop));
6338*7c478bd9Sstevel@tonic-gate 			return (NFS4_CHKSEQ_BAD);
6339*7c478bd9Sstevel@tonic-gate 		}
6340*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
6341*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "BAD SEQID: got %u expecting %u\n",
6342*7c478bd9Sstevel@tonic-gate 				rqst_seq, seqid));
6343*7c478bd9Sstevel@tonic-gate 		return (NFS4_CHKSEQ_BAD);
6344*7c478bd9Sstevel@tonic-gate 	}
6345*7c478bd9Sstevel@tonic-gate 
6346*7c478bd9Sstevel@tonic-gate 	/* Everything okay -- next expected */
6347*7c478bd9Sstevel@tonic-gate 	return (NFS4_CHKSEQ_OKAY);
6348*7c478bd9Sstevel@tonic-gate }
6349*7c478bd9Sstevel@tonic-gate 
6350*7c478bd9Sstevel@tonic-gate 
6351*7c478bd9Sstevel@tonic-gate static rfs4_chkseq_t
6352*7c478bd9Sstevel@tonic-gate rfs4_check_open_seqid(seqid4 seqid, rfs4_openowner_t *op, nfs_resop4 *resop)
6353*7c478bd9Sstevel@tonic-gate {
6354*7c478bd9Sstevel@tonic-gate 	rfs4_chkseq_t rc;
6355*7c478bd9Sstevel@tonic-gate 
6356*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(op->dbe);
6357*7c478bd9Sstevel@tonic-gate 	rc = rfs4_check_seqid(op->open_seqid, op->reply, seqid, resop, TRUE);
6358*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(op->dbe);
6359*7c478bd9Sstevel@tonic-gate 
6360*7c478bd9Sstevel@tonic-gate 	if (rc == NFS4_CHKSEQ_OKAY)
6361*7c478bd9Sstevel@tonic-gate 		rfs4_update_lease(op->client);
6362*7c478bd9Sstevel@tonic-gate 
6363*7c478bd9Sstevel@tonic-gate 	return (rc);
6364*7c478bd9Sstevel@tonic-gate }
6365*7c478bd9Sstevel@tonic-gate 
6366*7c478bd9Sstevel@tonic-gate static rfs4_chkseq_t
6367*7c478bd9Sstevel@tonic-gate rfs4_check_olo_seqid(seqid4 olo_seqid, rfs4_openowner_t *op,
6368*7c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop)
6369*7c478bd9Sstevel@tonic-gate {
6370*7c478bd9Sstevel@tonic-gate 	rfs4_chkseq_t rc;
6371*7c478bd9Sstevel@tonic-gate 
6372*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(op->dbe);
6373*7c478bd9Sstevel@tonic-gate 	rc = rfs4_check_seqid(op->open_seqid, op->reply,
6374*7c478bd9Sstevel@tonic-gate 		olo_seqid, resop, FALSE);
6375*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(op->dbe);
6376*7c478bd9Sstevel@tonic-gate 
6377*7c478bd9Sstevel@tonic-gate 	return (rc);
6378*7c478bd9Sstevel@tonic-gate }
6379*7c478bd9Sstevel@tonic-gate 
6380*7c478bd9Sstevel@tonic-gate static rfs4_chkseq_t
6381*7c478bd9Sstevel@tonic-gate rfs4_check_lock_seqid(seqid4 seqid, rfs4_lo_state_t *lp, nfs_resop4 *resop)
6382*7c478bd9Sstevel@tonic-gate {
6383*7c478bd9Sstevel@tonic-gate 	rfs4_chkseq_t rc = NFS4_CHKSEQ_OKAY;
6384*7c478bd9Sstevel@tonic-gate 
6385*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(lp->dbe);
6386*7c478bd9Sstevel@tonic-gate 	if (!lp->skip_seqid_check)
6387*7c478bd9Sstevel@tonic-gate 		rc = rfs4_check_seqid(lp->seqid, lp->reply,
6388*7c478bd9Sstevel@tonic-gate 			seqid, resop, TRUE);
6389*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(lp->dbe);
6390*7c478bd9Sstevel@tonic-gate 
6391*7c478bd9Sstevel@tonic-gate 	return (rc);
6392*7c478bd9Sstevel@tonic-gate }
6393*7c478bd9Sstevel@tonic-gate 
6394*7c478bd9Sstevel@tonic-gate static void
6395*7c478bd9Sstevel@tonic-gate rfs4_op_open(nfs_argop4 *argop, nfs_resop4 *resop,
6396*7c478bd9Sstevel@tonic-gate 	    struct svc_req *req, struct compound_state *cs)
6397*7c478bd9Sstevel@tonic-gate {
6398*7c478bd9Sstevel@tonic-gate 	OPEN4args *args = &argop->nfs_argop4_u.opopen;
6399*7c478bd9Sstevel@tonic-gate 	OPEN4res *resp = &resop->nfs_resop4_u.opopen;
6400*7c478bd9Sstevel@tonic-gate 	open_owner4 *owner = &args->owner;
6401*7c478bd9Sstevel@tonic-gate 	open_claim_type4 claim = args->claim;
6402*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp;
6403*7c478bd9Sstevel@tonic-gate 	rfs4_openowner_t *oo;
6404*7c478bd9Sstevel@tonic-gate 	bool_t create;
6405*7c478bd9Sstevel@tonic-gate 	bool_t replay = FALSE;
6406*7c478bd9Sstevel@tonic-gate 	int can_reclaim;
6407*7c478bd9Sstevel@tonic-gate 
6408*7c478bd9Sstevel@tonic-gate 
6409*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
6410*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
6411*7c478bd9Sstevel@tonic-gate 		return;
6412*7c478bd9Sstevel@tonic-gate 	}
6413*7c478bd9Sstevel@tonic-gate 
6414*7c478bd9Sstevel@tonic-gate 	/*
6415*7c478bd9Sstevel@tonic-gate 	 * Need to check clientid and lease expiration first based on
6416*7c478bd9Sstevel@tonic-gate 	 * error ordering and incrementing sequence id.
6417*7c478bd9Sstevel@tonic-gate 	 */
6418*7c478bd9Sstevel@tonic-gate 	cp = rfs4_findclient_by_id(owner->clientid, FALSE);
6419*7c478bd9Sstevel@tonic-gate 	if (cp == NULL) {
6420*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status =
6421*7c478bd9Sstevel@tonic-gate 			rfs4_check_clientid(&owner->clientid, 0);
6422*7c478bd9Sstevel@tonic-gate 		return;
6423*7c478bd9Sstevel@tonic-gate 	}
6424*7c478bd9Sstevel@tonic-gate 
6425*7c478bd9Sstevel@tonic-gate 	if (rfs4_lease_expired(cp)) {
6426*7c478bd9Sstevel@tonic-gate 		rfs4_client_close(cp);
6427*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
6428*7c478bd9Sstevel@tonic-gate 		return;
6429*7c478bd9Sstevel@tonic-gate 	}
6430*7c478bd9Sstevel@tonic-gate 	can_reclaim = cp->can_reclaim;
6431*7c478bd9Sstevel@tonic-gate 
6432*7c478bd9Sstevel@tonic-gate 	/*
6433*7c478bd9Sstevel@tonic-gate 	 * Find the open_owner for use from this point forward.  Take
6434*7c478bd9Sstevel@tonic-gate 	 * care in updating the sequence id based on the type of error
6435*7c478bd9Sstevel@tonic-gate 	 * being returned.
6436*7c478bd9Sstevel@tonic-gate 	 */
6437*7c478bd9Sstevel@tonic-gate retry:
6438*7c478bd9Sstevel@tonic-gate 	create = TRUE;
6439*7c478bd9Sstevel@tonic-gate 	oo = rfs4_findopenowner(owner, &create, args->seqid);
6440*7c478bd9Sstevel@tonic-gate 	if (oo == NULL) {
6441*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_STALE_CLIENTID;
6442*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
6443*7c478bd9Sstevel@tonic-gate 		return;
6444*7c478bd9Sstevel@tonic-gate 	}
6445*7c478bd9Sstevel@tonic-gate 
6446*7c478bd9Sstevel@tonic-gate 	/* Hold off access to the sequence space while the open is done */
6447*7c478bd9Sstevel@tonic-gate 	rfs4_sw_enter(&oo->oo_sw);
6448*7c478bd9Sstevel@tonic-gate 
6449*7c478bd9Sstevel@tonic-gate 	/*
6450*7c478bd9Sstevel@tonic-gate 	 * If the open_owner existed before at the server, then check
6451*7c478bd9Sstevel@tonic-gate 	 * the sequence id.
6452*7c478bd9Sstevel@tonic-gate 	 */
6453*7c478bd9Sstevel@tonic-gate 	if (!create && !oo->postpone_confirm) {
6454*7c478bd9Sstevel@tonic-gate 		switch (rfs4_check_open_seqid(args->seqid, oo, resop)) {
6455*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_BAD:
6456*7c478bd9Sstevel@tonic-gate 			if ((args->seqid > oo->open_seqid) &&
6457*7c478bd9Sstevel@tonic-gate 				oo->need_confirm) {
6458*7c478bd9Sstevel@tonic-gate 				rfs4_free_opens(oo, TRUE, FALSE);
6459*7c478bd9Sstevel@tonic-gate 				rfs4_sw_exit(&oo->oo_sw);
6460*7c478bd9Sstevel@tonic-gate 				rfs4_openowner_rele(oo);
6461*7c478bd9Sstevel@tonic-gate 				goto retry;
6462*7c478bd9Sstevel@tonic-gate 			}
6463*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_BAD_SEQID;
6464*7c478bd9Sstevel@tonic-gate 			goto out;
6465*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_REPLAY: /* replay of previous request */
6466*7c478bd9Sstevel@tonic-gate 			replay = TRUE;
6467*7c478bd9Sstevel@tonic-gate 			goto out;
6468*7c478bd9Sstevel@tonic-gate 		default:
6469*7c478bd9Sstevel@tonic-gate 			break;
6470*7c478bd9Sstevel@tonic-gate 		}
6471*7c478bd9Sstevel@tonic-gate 
6472*7c478bd9Sstevel@tonic-gate 		/*
6473*7c478bd9Sstevel@tonic-gate 		 * Sequence was ok and open owner exists
6474*7c478bd9Sstevel@tonic-gate 		 * check to see if we have yet to see an
6475*7c478bd9Sstevel@tonic-gate 		 * open_confirm.
6476*7c478bd9Sstevel@tonic-gate 		 */
6477*7c478bd9Sstevel@tonic-gate 		if (oo->need_confirm) {
6478*7c478bd9Sstevel@tonic-gate 			rfs4_free_opens(oo, TRUE, FALSE);
6479*7c478bd9Sstevel@tonic-gate 			rfs4_sw_exit(&oo->oo_sw);
6480*7c478bd9Sstevel@tonic-gate 			rfs4_openowner_rele(oo);
6481*7c478bd9Sstevel@tonic-gate 			goto retry;
6482*7c478bd9Sstevel@tonic-gate 		}
6483*7c478bd9Sstevel@tonic-gate 	}
6484*7c478bd9Sstevel@tonic-gate 	/* Grace only applies to regular-type OPENs */
6485*7c478bd9Sstevel@tonic-gate 	if (rfs4_clnt_in_grace(cp) &&
6486*7c478bd9Sstevel@tonic-gate 	    (claim == CLAIM_NULL || claim == CLAIM_DELEGATE_CUR)) {
6487*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_GRACE;
6488*7c478bd9Sstevel@tonic-gate 		goto out;
6489*7c478bd9Sstevel@tonic-gate 	}
6490*7c478bd9Sstevel@tonic-gate 
6491*7c478bd9Sstevel@tonic-gate 	/*
6492*7c478bd9Sstevel@tonic-gate 	 * If previous state at the server existed then can_reclaim
6493*7c478bd9Sstevel@tonic-gate 	 * will be set. If not reply NFS4ERR_NO_GRACE to the
6494*7c478bd9Sstevel@tonic-gate 	 * client.
6495*7c478bd9Sstevel@tonic-gate 	 */
6496*7c478bd9Sstevel@tonic-gate 	if (rfs4_clnt_in_grace(cp) && claim == CLAIM_PREVIOUS && !can_reclaim) {
6497*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NO_GRACE;
6498*7c478bd9Sstevel@tonic-gate 		goto out;
6499*7c478bd9Sstevel@tonic-gate 	}
6500*7c478bd9Sstevel@tonic-gate 
6501*7c478bd9Sstevel@tonic-gate 
6502*7c478bd9Sstevel@tonic-gate 	/*
6503*7c478bd9Sstevel@tonic-gate 	 * Reject the open if the client has missed the grace period
6504*7c478bd9Sstevel@tonic-gate 	 */
6505*7c478bd9Sstevel@tonic-gate 	if (!rfs4_clnt_in_grace(cp) && claim == CLAIM_PREVIOUS) {
6506*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NO_GRACE;
6507*7c478bd9Sstevel@tonic-gate 		goto out;
6508*7c478bd9Sstevel@tonic-gate 	}
6509*7c478bd9Sstevel@tonic-gate 
6510*7c478bd9Sstevel@tonic-gate 	/* Couple of up-front bookkeeping items */
6511*7c478bd9Sstevel@tonic-gate 	if (oo->need_confirm) {
6512*7c478bd9Sstevel@tonic-gate 		/*
6513*7c478bd9Sstevel@tonic-gate 		 * If this is a reclaim OPEN then we should not ask
6514*7c478bd9Sstevel@tonic-gate 		 * for a confirmation of the open_owner per the
6515*7c478bd9Sstevel@tonic-gate 		 * protocol specification.
6516*7c478bd9Sstevel@tonic-gate 		 */
6517*7c478bd9Sstevel@tonic-gate 		if (claim == CLAIM_PREVIOUS)
6518*7c478bd9Sstevel@tonic-gate 			oo->need_confirm = FALSE;
6519*7c478bd9Sstevel@tonic-gate 		else
6520*7c478bd9Sstevel@tonic-gate 			resp->rflags |= OPEN4_RESULT_CONFIRM;
6521*7c478bd9Sstevel@tonic-gate 	}
6522*7c478bd9Sstevel@tonic-gate 	resp->rflags |= OPEN4_RESULT_LOCKTYPE_POSIX;
6523*7c478bd9Sstevel@tonic-gate 
6524*7c478bd9Sstevel@tonic-gate 	/*
6525*7c478bd9Sstevel@tonic-gate 	 * If there is an unshared filesystem mounted on this vnode,
6526*7c478bd9Sstevel@tonic-gate 	 * do not allow to open/create in this directory.
6527*7c478bd9Sstevel@tonic-gate 	 */
6528*7c478bd9Sstevel@tonic-gate 	if (vn_ismntpt(cs->vp)) {
6529*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_ACCESS;
6530*7c478bd9Sstevel@tonic-gate 		goto out;
6531*7c478bd9Sstevel@tonic-gate 	}
6532*7c478bd9Sstevel@tonic-gate 
6533*7c478bd9Sstevel@tonic-gate 	/*
6534*7c478bd9Sstevel@tonic-gate 	 * access must READ, WRITE, or BOTH.  No access is invalid.
6535*7c478bd9Sstevel@tonic-gate 	 * deny can be READ, WRITE, BOTH, or NONE.
6536*7c478bd9Sstevel@tonic-gate 	 * bits not defined for access/deny are invalid.
6537*7c478bd9Sstevel@tonic-gate 	 */
6538*7c478bd9Sstevel@tonic-gate 	if (! (args->share_access & OPEN4_SHARE_ACCESS_BOTH) ||
6539*7c478bd9Sstevel@tonic-gate 	    (args->share_access & ~OPEN4_SHARE_ACCESS_BOTH) ||
6540*7c478bd9Sstevel@tonic-gate 	    (args->share_deny & ~OPEN4_SHARE_DENY_BOTH)) {
6541*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
6542*7c478bd9Sstevel@tonic-gate 		goto out;
6543*7c478bd9Sstevel@tonic-gate 	}
6544*7c478bd9Sstevel@tonic-gate 
6545*7c478bd9Sstevel@tonic-gate 
6546*7c478bd9Sstevel@tonic-gate 	/*
6547*7c478bd9Sstevel@tonic-gate 	 * make sure attrset is zero before response is built.
6548*7c478bd9Sstevel@tonic-gate 	 */
6549*7c478bd9Sstevel@tonic-gate 	resp->attrset = 0;
6550*7c478bd9Sstevel@tonic-gate 
6551*7c478bd9Sstevel@tonic-gate 	switch (claim) {
6552*7c478bd9Sstevel@tonic-gate 	case CLAIM_NULL:
6553*7c478bd9Sstevel@tonic-gate 		rfs4_do_opennull(cs, req, args, oo, resp);
6554*7c478bd9Sstevel@tonic-gate 	    break;
6555*7c478bd9Sstevel@tonic-gate 	case CLAIM_PREVIOUS:
6556*7c478bd9Sstevel@tonic-gate 		rfs4_do_openprev(cs, req, args, oo, resp);
6557*7c478bd9Sstevel@tonic-gate 	    break;
6558*7c478bd9Sstevel@tonic-gate 	case CLAIM_DELEGATE_CUR:
6559*7c478bd9Sstevel@tonic-gate 		rfs4_do_opendelcur(cs, req, args, oo, resp);
6560*7c478bd9Sstevel@tonic-gate 	    break;
6561*7c478bd9Sstevel@tonic-gate 	case CLAIM_DELEGATE_PREV:
6562*7c478bd9Sstevel@tonic-gate 		rfs4_do_opendelprev(cs, req, args, oo, resp);
6563*7c478bd9Sstevel@tonic-gate 	    break;
6564*7c478bd9Sstevel@tonic-gate 	default:
6565*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_INVAL;
6566*7c478bd9Sstevel@tonic-gate 		break;
6567*7c478bd9Sstevel@tonic-gate 	}
6568*7c478bd9Sstevel@tonic-gate 
6569*7c478bd9Sstevel@tonic-gate out:
6570*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(cp);
6571*7c478bd9Sstevel@tonic-gate 
6572*7c478bd9Sstevel@tonic-gate 	/* Catch sequence id handling here to make it a little easier */
6573*7c478bd9Sstevel@tonic-gate 	switch (resp->status) {
6574*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_BADXDR:
6575*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_BAD_SEQID:
6576*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_BAD_STATEID:
6577*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_NOFILEHANDLE:
6578*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_RESOURCE:
6579*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_STALE_CLIENTID:
6580*7c478bd9Sstevel@tonic-gate 	case NFS4ERR_STALE_STATEID:
6581*7c478bd9Sstevel@tonic-gate 		/*
6582*7c478bd9Sstevel@tonic-gate 		 * The protocol states that if any of these errors are
6583*7c478bd9Sstevel@tonic-gate 		 * being returned, the sequence id should not be
6584*7c478bd9Sstevel@tonic-gate 		 * incremented.  Any other return requires an
6585*7c478bd9Sstevel@tonic-gate 		 * increment.
6586*7c478bd9Sstevel@tonic-gate 		 */
6587*7c478bd9Sstevel@tonic-gate 		break;
6588*7c478bd9Sstevel@tonic-gate 	default:
6589*7c478bd9Sstevel@tonic-gate 		/* Always update the lease in this case */
6590*7c478bd9Sstevel@tonic-gate 		rfs4_update_lease(oo->client);
6591*7c478bd9Sstevel@tonic-gate 
6592*7c478bd9Sstevel@tonic-gate 		/* Regular response - copy the result */
6593*7c478bd9Sstevel@tonic-gate 		if (!replay)
6594*7c478bd9Sstevel@tonic-gate 			rfs4_update_open_resp(oo, resop, &cs->fh);
6595*7c478bd9Sstevel@tonic-gate 
6596*7c478bd9Sstevel@tonic-gate 		/*
6597*7c478bd9Sstevel@tonic-gate 		 * REPLAY case: Only if the previous response was OK
6598*7c478bd9Sstevel@tonic-gate 		 * do we copy the filehandle.  If not OK, no
6599*7c478bd9Sstevel@tonic-gate 		 * filehandle to copy.
6600*7c478bd9Sstevel@tonic-gate 		 */
6601*7c478bd9Sstevel@tonic-gate 		if (replay == TRUE &&
6602*7c478bd9Sstevel@tonic-gate 		    resp->status == NFS4_OK &&
6603*7c478bd9Sstevel@tonic-gate 		    oo->reply_fh.nfs_fh4_val) {
6604*7c478bd9Sstevel@tonic-gate 			/*
6605*7c478bd9Sstevel@tonic-gate 			 * If this is a replay, we must restore the
6606*7c478bd9Sstevel@tonic-gate 			 * current filehandle/vp to that of what was
6607*7c478bd9Sstevel@tonic-gate 			 * returned originally.  Try our best to do
6608*7c478bd9Sstevel@tonic-gate 			 * it.
6609*7c478bd9Sstevel@tonic-gate 			 */
6610*7c478bd9Sstevel@tonic-gate 			nfs_fh4_fmt_t *fh_fmtp =
6611*7c478bd9Sstevel@tonic-gate 				(nfs_fh4_fmt_t *)oo->reply_fh.nfs_fh4_val;
6612*7c478bd9Sstevel@tonic-gate 
6613*7c478bd9Sstevel@tonic-gate 			cs->exi = checkexport4(&fh_fmtp->fh4_fsid,
6614*7c478bd9Sstevel@tonic-gate 				(fid_t *)&fh_fmtp->fh4_xlen, NULL);
6615*7c478bd9Sstevel@tonic-gate 
6616*7c478bd9Sstevel@tonic-gate 			if (cs->exi == NULL) {
6617*7c478bd9Sstevel@tonic-gate 				resp->status = NFS4ERR_STALE;
6618*7c478bd9Sstevel@tonic-gate 				goto finish;
6619*7c478bd9Sstevel@tonic-gate 			}
6620*7c478bd9Sstevel@tonic-gate 
6621*7c478bd9Sstevel@tonic-gate 			VN_RELE(cs->vp);
6622*7c478bd9Sstevel@tonic-gate 
6623*7c478bd9Sstevel@tonic-gate 			cs->vp = nfs4_fhtovp(&oo->reply_fh, cs->exi,
6624*7c478bd9Sstevel@tonic-gate 				&resp->status);
6625*7c478bd9Sstevel@tonic-gate 
6626*7c478bd9Sstevel@tonic-gate 			if (cs->vp == NULL)
6627*7c478bd9Sstevel@tonic-gate 				goto finish;
6628*7c478bd9Sstevel@tonic-gate 
6629*7c478bd9Sstevel@tonic-gate 			nfs_fh4_copy(&oo->reply_fh, &cs->fh);
6630*7c478bd9Sstevel@tonic-gate 		}
6631*7c478bd9Sstevel@tonic-gate 
6632*7c478bd9Sstevel@tonic-gate 		/*
6633*7c478bd9Sstevel@tonic-gate 		 * If this was a replay, no need to update the
6634*7c478bd9Sstevel@tonic-gate 		 * sequence id. If the open_owner was not created on
6635*7c478bd9Sstevel@tonic-gate 		 * this pass, then update.  The first use of an
6636*7c478bd9Sstevel@tonic-gate 		 * open_owner will not bump the sequence id.
6637*7c478bd9Sstevel@tonic-gate 		 */
6638*7c478bd9Sstevel@tonic-gate 		if (replay == FALSE && !create)
6639*7c478bd9Sstevel@tonic-gate 			rfs4_update_open_sequence(oo);
6640*7c478bd9Sstevel@tonic-gate 		/*
6641*7c478bd9Sstevel@tonic-gate 		 * If the client is receiving an error and the
6642*7c478bd9Sstevel@tonic-gate 		 * open_owner needs to be confirmed, there is no way
6643*7c478bd9Sstevel@tonic-gate 		 * to notify the client of this fact ignoring the fact
6644*7c478bd9Sstevel@tonic-gate 		 * that the server has no method of returning a
6645*7c478bd9Sstevel@tonic-gate 		 * stateid to confirm.  Therefore, the server needs to
6646*7c478bd9Sstevel@tonic-gate 		 * mark this open_owner in a way as to avoid the
6647*7c478bd9Sstevel@tonic-gate 		 * sequence id checking the next time the client uses
6648*7c478bd9Sstevel@tonic-gate 		 * this open_owner.
6649*7c478bd9Sstevel@tonic-gate 		 */
6650*7c478bd9Sstevel@tonic-gate 		if (resp->status != NFS4_OK && oo->need_confirm)
6651*7c478bd9Sstevel@tonic-gate 			oo->postpone_confirm = TRUE;
6652*7c478bd9Sstevel@tonic-gate 		/*
6653*7c478bd9Sstevel@tonic-gate 		 * If OK response then clear the postpone flag and
6654*7c478bd9Sstevel@tonic-gate 		 * reset the sequence id to keep in sync with the
6655*7c478bd9Sstevel@tonic-gate 		 * client.
6656*7c478bd9Sstevel@tonic-gate 		 */
6657*7c478bd9Sstevel@tonic-gate 		if (resp->status == NFS4_OK && oo->postpone_confirm) {
6658*7c478bd9Sstevel@tonic-gate 			oo->postpone_confirm = FALSE;
6659*7c478bd9Sstevel@tonic-gate 			oo->open_seqid = args->seqid;
6660*7c478bd9Sstevel@tonic-gate 		}
6661*7c478bd9Sstevel@tonic-gate 		break;
6662*7c478bd9Sstevel@tonic-gate 	}
6663*7c478bd9Sstevel@tonic-gate 
6664*7c478bd9Sstevel@tonic-gate finish:
6665*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status;
6666*7c478bd9Sstevel@tonic-gate 
6667*7c478bd9Sstevel@tonic-gate 	rfs4_sw_exit(&oo->oo_sw);
6668*7c478bd9Sstevel@tonic-gate 	rfs4_openowner_rele(oo);
6669*7c478bd9Sstevel@tonic-gate }
6670*7c478bd9Sstevel@tonic-gate 
6671*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6672*7c478bd9Sstevel@tonic-gate void
6673*7c478bd9Sstevel@tonic-gate rfs4_op_open_confirm(nfs_argop4 *argop, nfs_resop4 *resop,
6674*7c478bd9Sstevel@tonic-gate 		    struct svc_req *req, struct compound_state *cs)
6675*7c478bd9Sstevel@tonic-gate {
6676*7c478bd9Sstevel@tonic-gate 	OPEN_CONFIRM4args *args = &argop->nfs_argop4_u.opopen_confirm;
6677*7c478bd9Sstevel@tonic-gate 	OPEN_CONFIRM4res *resp = &resop->nfs_resop4_u.opopen_confirm;
6678*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *sp;
6679*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
6680*7c478bd9Sstevel@tonic-gate 
6681*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
6682*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
6683*7c478bd9Sstevel@tonic-gate 		return;
6684*7c478bd9Sstevel@tonic-gate 	}
6685*7c478bd9Sstevel@tonic-gate 
6686*7c478bd9Sstevel@tonic-gate 	status = rfs4_get_state(&args->open_stateid, &sp, RFS4_DBS_VALID);
6687*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
6688*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = status;
6689*7c478bd9Sstevel@tonic-gate 		return;
6690*7c478bd9Sstevel@tonic-gate 	}
6691*7c478bd9Sstevel@tonic-gate 
6692*7c478bd9Sstevel@tonic-gate 	/* Ensure specified filehandle matches */
6693*7c478bd9Sstevel@tonic-gate 	if (cs->vp != sp->finfo->vp) {
6694*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(sp);
6695*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
6696*7c478bd9Sstevel@tonic-gate 		return;
6697*7c478bd9Sstevel@tonic-gate 	}
6698*7c478bd9Sstevel@tonic-gate 
6699*7c478bd9Sstevel@tonic-gate 	/* hold off other access to open_owner while we tinker */
6700*7c478bd9Sstevel@tonic-gate 	rfs4_sw_enter(&sp->owner->oo_sw);
6701*7c478bd9Sstevel@tonic-gate 
6702*7c478bd9Sstevel@tonic-gate 	switch (rfs4_check_stateid_seqid(sp, &args->open_stateid)) {
6703*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OKAY:
6704*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_open_seqid(args->seqid, sp->owner,
6705*7c478bd9Sstevel@tonic-gate 			resop) != 0) {
6706*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
6707*7c478bd9Sstevel@tonic-gate 			break;
6708*7c478bd9Sstevel@tonic-gate 		}
6709*7c478bd9Sstevel@tonic-gate 		/*
6710*7c478bd9Sstevel@tonic-gate 		 * If it is the appropriate stateid and determined to
6711*7c478bd9Sstevel@tonic-gate 		 * be "OKAY" then this means that the stateid does not
6712*7c478bd9Sstevel@tonic-gate 		 * need to be confirmed and the client is in error for
6713*7c478bd9Sstevel@tonic-gate 		 * sending an OPEN_CONFIRM.
6714*7c478bd9Sstevel@tonic-gate 		 */
6715*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
6716*7c478bd9Sstevel@tonic-gate 		break;
6717*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OLD:
6718*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
6719*7c478bd9Sstevel@tonic-gate 		break;
6720*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_BAD:
6721*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
6722*7c478bd9Sstevel@tonic-gate 		break;
6723*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_EXPIRED:
6724*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
6725*7c478bd9Sstevel@tonic-gate 		break;
6726*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_CLOSED:
6727*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
6728*7c478bd9Sstevel@tonic-gate 		break;
6729*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_REPLAY:
6730*7c478bd9Sstevel@tonic-gate 		switch (rfs4_check_open_seqid(args->seqid, sp->owner, resop)) {
6731*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_OKAY:
6732*7c478bd9Sstevel@tonic-gate 			/*
6733*7c478bd9Sstevel@tonic-gate 			 * This is replayed stateid; if seqid matches
6734*7c478bd9Sstevel@tonic-gate 			 * next expected, then client is using wrong seqid.
6735*7c478bd9Sstevel@tonic-gate 			 */
6736*7c478bd9Sstevel@tonic-gate 			/* fall through */
6737*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_BAD:
6738*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
6739*7c478bd9Sstevel@tonic-gate 			break;
6740*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_REPLAY:
6741*7c478bd9Sstevel@tonic-gate 			/*
6742*7c478bd9Sstevel@tonic-gate 			 * Note this case is the duplicate case so
6743*7c478bd9Sstevel@tonic-gate 			 * resp->status is already set.
6744*7c478bd9Sstevel@tonic-gate 			 */
6745*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status;
6746*7c478bd9Sstevel@tonic-gate 			rfs4_update_lease(sp->owner->client);
6747*7c478bd9Sstevel@tonic-gate 			break;
6748*7c478bd9Sstevel@tonic-gate 		}
6749*7c478bd9Sstevel@tonic-gate 		break;
6750*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_UNCONFIRMED:
6751*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_open_seqid(args->seqid, sp->owner,
6752*7c478bd9Sstevel@tonic-gate 			resop) != NFS4_CHKSEQ_OKAY) {
6753*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
6754*7c478bd9Sstevel@tonic-gate 			break;
6755*7c478bd9Sstevel@tonic-gate 		}
6756*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4_OK;
6757*7c478bd9Sstevel@tonic-gate 
6758*7c478bd9Sstevel@tonic-gate 		next_stateid(&sp->stateid);
6759*7c478bd9Sstevel@tonic-gate 		resp->open_stateid = sp->stateid.stateid;
6760*7c478bd9Sstevel@tonic-gate 		sp->owner->need_confirm = FALSE;
6761*7c478bd9Sstevel@tonic-gate 		rfs4_update_lease(sp->owner->client);
6762*7c478bd9Sstevel@tonic-gate 		rfs4_update_open_sequence(sp->owner);
6763*7c478bd9Sstevel@tonic-gate 		rfs4_update_open_resp(sp->owner, resop, NULL);
6764*7c478bd9Sstevel@tonic-gate 		break;
6765*7c478bd9Sstevel@tonic-gate 	default:
6766*7c478bd9Sstevel@tonic-gate 		ASSERT(FALSE);
6767*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_SERVERFAULT;
6768*7c478bd9Sstevel@tonic-gate 		break;
6769*7c478bd9Sstevel@tonic-gate 	}
6770*7c478bd9Sstevel@tonic-gate 	rfs4_sw_exit(&sp->owner->oo_sw);
6771*7c478bd9Sstevel@tonic-gate 	rfs4_state_rele(sp);
6772*7c478bd9Sstevel@tonic-gate }
6773*7c478bd9Sstevel@tonic-gate 
6774*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6775*7c478bd9Sstevel@tonic-gate void
6776*7c478bd9Sstevel@tonic-gate rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop,
6777*7c478bd9Sstevel@tonic-gate 		    struct svc_req *req, struct compound_state *cs)
6778*7c478bd9Sstevel@tonic-gate {
6779*7c478bd9Sstevel@tonic-gate 	OPEN_DOWNGRADE4args *args = &argop->nfs_argop4_u.opopen_downgrade;
6780*7c478bd9Sstevel@tonic-gate 	OPEN_DOWNGRADE4res *resp = &resop->nfs_resop4_u.opopen_downgrade;
6781*7c478bd9Sstevel@tonic-gate 	uint32_t access = args->share_access;
6782*7c478bd9Sstevel@tonic-gate 	uint32_t deny = args->share_deny;
6783*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
6784*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *sp;
6785*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp;
6786*7c478bd9Sstevel@tonic-gate 
6787*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
6788*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
6789*7c478bd9Sstevel@tonic-gate 		return;
6790*7c478bd9Sstevel@tonic-gate 	}
6791*7c478bd9Sstevel@tonic-gate 
6792*7c478bd9Sstevel@tonic-gate 	status = rfs4_get_state(&args->open_stateid, &sp, RFS4_DBS_VALID);
6793*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
6794*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = status;
6795*7c478bd9Sstevel@tonic-gate 		return;
6796*7c478bd9Sstevel@tonic-gate 	}
6797*7c478bd9Sstevel@tonic-gate 
6798*7c478bd9Sstevel@tonic-gate 	/* Ensure specified filehandle matches */
6799*7c478bd9Sstevel@tonic-gate 	if (cs->vp != sp->finfo->vp) {
6800*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(sp);
6801*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
6802*7c478bd9Sstevel@tonic-gate 		return;
6803*7c478bd9Sstevel@tonic-gate 	}
6804*7c478bd9Sstevel@tonic-gate 
6805*7c478bd9Sstevel@tonic-gate 	/* hold off other access to open_owner while we tinker */
6806*7c478bd9Sstevel@tonic-gate 	rfs4_sw_enter(&sp->owner->oo_sw);
6807*7c478bd9Sstevel@tonic-gate 
6808*7c478bd9Sstevel@tonic-gate 	switch (rfs4_check_stateid_seqid(sp, &args->open_stateid)) {
6809*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OKAY:
6810*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_open_seqid(args->seqid, sp->owner,
6811*7c478bd9Sstevel@tonic-gate 			resop) != NFS4_CHKSEQ_OKAY) {
6812*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
6813*7c478bd9Sstevel@tonic-gate 			goto end;
6814*7c478bd9Sstevel@tonic-gate 		}
6815*7c478bd9Sstevel@tonic-gate 		break;
6816*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OLD:
6817*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
6818*7c478bd9Sstevel@tonic-gate 		goto end;
6819*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_BAD:
6820*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
6821*7c478bd9Sstevel@tonic-gate 		goto end;
6822*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_EXPIRED:
6823*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
6824*7c478bd9Sstevel@tonic-gate 		goto end;
6825*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_CLOSED:
6826*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
6827*7c478bd9Sstevel@tonic-gate 		goto end;
6828*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_UNCONFIRMED:
6829*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
6830*7c478bd9Sstevel@tonic-gate 		goto end;
6831*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_REPLAY:
6832*7c478bd9Sstevel@tonic-gate 		/* Check the sequence id for the open owner */
6833*7c478bd9Sstevel@tonic-gate 		switch (rfs4_check_open_seqid(args->seqid, sp->owner, resop)) {
6834*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_OKAY:
6835*7c478bd9Sstevel@tonic-gate 			/*
6836*7c478bd9Sstevel@tonic-gate 			 * This is replayed stateid; if seqid matches
6837*7c478bd9Sstevel@tonic-gate 			 * next expected, then client is using wrong seqid.
6838*7c478bd9Sstevel@tonic-gate 			 */
6839*7c478bd9Sstevel@tonic-gate 			/* fall through */
6840*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_BAD:
6841*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
6842*7c478bd9Sstevel@tonic-gate 			goto end;
6843*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_REPLAY:
6844*7c478bd9Sstevel@tonic-gate 			/*
6845*7c478bd9Sstevel@tonic-gate 			 * Note this case is the duplicate case so
6846*7c478bd9Sstevel@tonic-gate 			 * resp->status is already set.
6847*7c478bd9Sstevel@tonic-gate 			 */
6848*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status;
6849*7c478bd9Sstevel@tonic-gate 			rfs4_update_lease(sp->owner->client);
6850*7c478bd9Sstevel@tonic-gate 			goto end;
6851*7c478bd9Sstevel@tonic-gate 		}
6852*7c478bd9Sstevel@tonic-gate 		break;
6853*7c478bd9Sstevel@tonic-gate 	default:
6854*7c478bd9Sstevel@tonic-gate 		ASSERT(FALSE);
6855*7c478bd9Sstevel@tonic-gate 		break;
6856*7c478bd9Sstevel@tonic-gate 	}
6857*7c478bd9Sstevel@tonic-gate 
6858*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(sp->dbe);
6859*7c478bd9Sstevel@tonic-gate 	/*
6860*7c478bd9Sstevel@tonic-gate 	 * Check that the new access modes and deny modes are valid.
6861*7c478bd9Sstevel@tonic-gate 	 * Check that no invalid bits are set.
6862*7c478bd9Sstevel@tonic-gate 	 */
6863*7c478bd9Sstevel@tonic-gate 	if ((access & ~(OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WRITE)) ||
6864*7c478bd9Sstevel@tonic-gate 	    (deny & ~(OPEN4_SHARE_DENY_READ | OPEN4_SHARE_DENY_READ))) {
6865*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
6866*7c478bd9Sstevel@tonic-gate 		rfs4_update_open_sequence(sp->owner);
6867*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(sp->dbe);
6868*7c478bd9Sstevel@tonic-gate 		goto end;
6869*7c478bd9Sstevel@tonic-gate 	}
6870*7c478bd9Sstevel@tonic-gate 
6871*7c478bd9Sstevel@tonic-gate 	/*
6872*7c478bd9Sstevel@tonic-gate 	 * The new modes must be a subset of the current modes and
6873*7c478bd9Sstevel@tonic-gate 	 * the access must specify at least one mode. To test that
6874*7c478bd9Sstevel@tonic-gate 	 * the new mode is a subset of the current modes we bitwise
6875*7c478bd9Sstevel@tonic-gate 	 * AND them together and check that the result equals the new
6876*7c478bd9Sstevel@tonic-gate 	 * mode. For example:
6877*7c478bd9Sstevel@tonic-gate 	 * New mode, access == R and current mode, sp->share_access  == RW
6878*7c478bd9Sstevel@tonic-gate 	 * access & sp->share_access == R == access, so the new access mode
6879*7c478bd9Sstevel@tonic-gate 	 * is valid. Consider access == RW, sp->share_access = R
6880*7c478bd9Sstevel@tonic-gate 	 * access & sp->share_access == R != access, so the new access mode
6881*7c478bd9Sstevel@tonic-gate 	 * is invalid.
6882*7c478bd9Sstevel@tonic-gate 	 */
6883*7c478bd9Sstevel@tonic-gate 	if ((access & sp->share_access) != access ||
6884*7c478bd9Sstevel@tonic-gate 	    (deny & sp->share_deny) != deny ||
6885*7c478bd9Sstevel@tonic-gate 	    (access &
6886*7c478bd9Sstevel@tonic-gate 	    (OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WRITE)) == 0) {
6887*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
6888*7c478bd9Sstevel@tonic-gate 		rfs4_update_open_sequence(sp->owner);
6889*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(sp->dbe);
6890*7c478bd9Sstevel@tonic-gate 		goto end;
6891*7c478bd9Sstevel@tonic-gate 	}
6892*7c478bd9Sstevel@tonic-gate 
6893*7c478bd9Sstevel@tonic-gate 	/*
6894*7c478bd9Sstevel@tonic-gate 	 * Release any share locks associated with this stateID.
6895*7c478bd9Sstevel@tonic-gate 	 * Strictly speaking, this violates the spec because the
6896*7c478bd9Sstevel@tonic-gate 	 * spec effectively requires that open downgrade be atomic.
6897*7c478bd9Sstevel@tonic-gate 	 * At present, fs_shrlock does not have this capability.
6898*7c478bd9Sstevel@tonic-gate 	 */
6899*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(sp->dbe);
6900*7c478bd9Sstevel@tonic-gate 	rfs4_unshare(sp);
6901*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(sp->dbe);
6902*7c478bd9Sstevel@tonic-gate 
6903*7c478bd9Sstevel@tonic-gate 	fp = sp->finfo;
6904*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(fp->dbe);
6905*7c478bd9Sstevel@tonic-gate 
6906*7c478bd9Sstevel@tonic-gate 	/*
6907*7c478bd9Sstevel@tonic-gate 	 * If the current mode has deny read and the new mode
6908*7c478bd9Sstevel@tonic-gate 	 * does not, decrement the number of deny read mode bits
6909*7c478bd9Sstevel@tonic-gate 	 * and if it goes to zero turn off the deny read bit
6910*7c478bd9Sstevel@tonic-gate 	 * on the file.
6911*7c478bd9Sstevel@tonic-gate 	 */
6912*7c478bd9Sstevel@tonic-gate 	if ((sp->share_deny & OPEN4_SHARE_DENY_READ) &&
6913*7c478bd9Sstevel@tonic-gate 	    (deny & OPEN4_SHARE_DENY_READ) == 0) {
6914*7c478bd9Sstevel@tonic-gate 		fp->deny_read--;
6915*7c478bd9Sstevel@tonic-gate 		if (fp->deny_read == 0)
6916*7c478bd9Sstevel@tonic-gate 			fp->share_deny &= ~OPEN4_SHARE_DENY_READ;
6917*7c478bd9Sstevel@tonic-gate 	}
6918*7c478bd9Sstevel@tonic-gate 
6919*7c478bd9Sstevel@tonic-gate 	/*
6920*7c478bd9Sstevel@tonic-gate 	 * If the current mode has deny write and the new mode
6921*7c478bd9Sstevel@tonic-gate 	 * does not, decrement the number of deny write mode bits
6922*7c478bd9Sstevel@tonic-gate 	 * and if it goes to zero turn off the deny write bit
6923*7c478bd9Sstevel@tonic-gate 	 * on the file.
6924*7c478bd9Sstevel@tonic-gate 	 */
6925*7c478bd9Sstevel@tonic-gate 	if ((sp->share_deny & OPEN4_SHARE_DENY_WRITE) &&
6926*7c478bd9Sstevel@tonic-gate 	    (deny & OPEN4_SHARE_DENY_WRITE) == 0) {
6927*7c478bd9Sstevel@tonic-gate 		fp->deny_write--;
6928*7c478bd9Sstevel@tonic-gate 		if (fp->deny_write == 0)
6929*7c478bd9Sstevel@tonic-gate 			fp->share_deny &= ~OPEN4_SHARE_DENY_WRITE;
6930*7c478bd9Sstevel@tonic-gate 	}
6931*7c478bd9Sstevel@tonic-gate 
6932*7c478bd9Sstevel@tonic-gate 	/*
6933*7c478bd9Sstevel@tonic-gate 	 * If the current mode has access read and the new mode
6934*7c478bd9Sstevel@tonic-gate 	 * does not, decrement the number of access read mode bits
6935*7c478bd9Sstevel@tonic-gate 	 * and if it goes to zero turn off the access read bit
6936*7c478bd9Sstevel@tonic-gate 	 * on the file.
6937*7c478bd9Sstevel@tonic-gate 	 */
6938*7c478bd9Sstevel@tonic-gate 	if ((sp->share_access & OPEN4_SHARE_ACCESS_READ) &&
6939*7c478bd9Sstevel@tonic-gate 	    (access & OPEN4_SHARE_ACCESS_READ) == 0) {
6940*7c478bd9Sstevel@tonic-gate 		fp->access_read--;
6941*7c478bd9Sstevel@tonic-gate 		if (fp->access_read == 0)
6942*7c478bd9Sstevel@tonic-gate 			fp->share_access &= ~OPEN4_SHARE_ACCESS_READ;
6943*7c478bd9Sstevel@tonic-gate 	}
6944*7c478bd9Sstevel@tonic-gate 
6945*7c478bd9Sstevel@tonic-gate 	/*
6946*7c478bd9Sstevel@tonic-gate 	 * If the current mode has access write and the new mode
6947*7c478bd9Sstevel@tonic-gate 	 * does not, decrement the number of access write mode bits
6948*7c478bd9Sstevel@tonic-gate 	 * and if it goes to zero turn off the access write bit
6949*7c478bd9Sstevel@tonic-gate 	 * on the file.
6950*7c478bd9Sstevel@tonic-gate 	 */
6951*7c478bd9Sstevel@tonic-gate 	if ((sp->share_access & OPEN4_SHARE_ACCESS_WRITE) &&
6952*7c478bd9Sstevel@tonic-gate 	    (access & OPEN4_SHARE_ACCESS_WRITE) == 0) {
6953*7c478bd9Sstevel@tonic-gate 		fp->access_write--;
6954*7c478bd9Sstevel@tonic-gate 		if (fp->access_write == 0)
6955*7c478bd9Sstevel@tonic-gate 			fp->share_deny &= ~OPEN4_SHARE_ACCESS_WRITE;
6956*7c478bd9Sstevel@tonic-gate 	}
6957*7c478bd9Sstevel@tonic-gate 
6958*7c478bd9Sstevel@tonic-gate 	/* Set the new access and deny modes */
6959*7c478bd9Sstevel@tonic-gate 	sp->share_access = access;
6960*7c478bd9Sstevel@tonic-gate 	sp->share_deny = deny;
6961*7c478bd9Sstevel@tonic-gate 	/* Check that the file is still accessible */
6962*7c478bd9Sstevel@tonic-gate 	ASSERT(fp->share_access);
6963*7c478bd9Sstevel@tonic-gate 
6964*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(fp->dbe);
6965*7c478bd9Sstevel@tonic-gate 
6966*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(sp->dbe);
6967*7c478bd9Sstevel@tonic-gate 	if ((status = rfs4_share(sp)) != NFS4_OK) {
6968*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_SERVERFAULT;
6969*7c478bd9Sstevel@tonic-gate 		rfs4_update_open_sequence(sp->owner);
6970*7c478bd9Sstevel@tonic-gate 		goto end;
6971*7c478bd9Sstevel@tonic-gate 	}
6972*7c478bd9Sstevel@tonic-gate 
6973*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(sp->dbe);
6974*7c478bd9Sstevel@tonic-gate 
6975*7c478bd9Sstevel@tonic-gate 	/* Update the stateid */
6976*7c478bd9Sstevel@tonic-gate 	next_stateid(&sp->stateid);
6977*7c478bd9Sstevel@tonic-gate 	resp->open_stateid = sp->stateid.stateid;
6978*7c478bd9Sstevel@tonic-gate 
6979*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(sp->dbe);
6980*7c478bd9Sstevel@tonic-gate 
6981*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = NFS4_OK;
6982*7c478bd9Sstevel@tonic-gate 	/* Update the lease */
6983*7c478bd9Sstevel@tonic-gate 	rfs4_update_lease(sp->owner->client);
6984*7c478bd9Sstevel@tonic-gate 	/* And the sequence */
6985*7c478bd9Sstevel@tonic-gate 	rfs4_update_open_sequence(sp->owner);
6986*7c478bd9Sstevel@tonic-gate 	rfs4_update_open_resp(sp->owner, resop, NULL);
6987*7c478bd9Sstevel@tonic-gate 
6988*7c478bd9Sstevel@tonic-gate end:
6989*7c478bd9Sstevel@tonic-gate 	rfs4_sw_exit(&sp->owner->oo_sw);
6990*7c478bd9Sstevel@tonic-gate 	rfs4_state_rele(sp);
6991*7c478bd9Sstevel@tonic-gate }
6992*7c478bd9Sstevel@tonic-gate 
6993*7c478bd9Sstevel@tonic-gate /*
6994*7c478bd9Sstevel@tonic-gate  * The logic behind this function is detailed in the NFSv4 RFC in the
6995*7c478bd9Sstevel@tonic-gate  * SETCLIENTID operation description under IMPLEMENTATION.  Refer to
6996*7c478bd9Sstevel@tonic-gate  * that section for explicit guidance to server behavior for
6997*7c478bd9Sstevel@tonic-gate  * SETCLIENTID.
6998*7c478bd9Sstevel@tonic-gate  */
6999*7c478bd9Sstevel@tonic-gate void
7000*7c478bd9Sstevel@tonic-gate rfs4_op_setclientid(nfs_argop4 *argop, nfs_resop4 *resop,
7001*7c478bd9Sstevel@tonic-gate 		    struct svc_req *req, struct compound_state *cs)
7002*7c478bd9Sstevel@tonic-gate {
7003*7c478bd9Sstevel@tonic-gate 	SETCLIENTID4args *args = &argop->nfs_argop4_u.opsetclientid;
7004*7c478bd9Sstevel@tonic-gate 	SETCLIENTID4res *res = &resop->nfs_resop4_u.opsetclientid;
7005*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp, *newcp, *cp_confirmed, *cp_unconfirmed;
7006*7c478bd9Sstevel@tonic-gate 	bool_t create = TRUE;
7007*7c478bd9Sstevel@tonic-gate 	char *addr, *netid;
7008*7c478bd9Sstevel@tonic-gate 	int len;
7009*7c478bd9Sstevel@tonic-gate 
7010*7c478bd9Sstevel@tonic-gate retry:
7011*7c478bd9Sstevel@tonic-gate 	newcp = cp_confirmed = cp_unconfirmed = NULL;
7012*7c478bd9Sstevel@tonic-gate 
7013*7c478bd9Sstevel@tonic-gate 	/*
7014*7c478bd9Sstevel@tonic-gate 	 * In search of an EXISTING client matching the incoming
7015*7c478bd9Sstevel@tonic-gate 	 * request to establish a new client identifier at the server
7016*7c478bd9Sstevel@tonic-gate 	 */
7017*7c478bd9Sstevel@tonic-gate 	create = TRUE;
7018*7c478bd9Sstevel@tonic-gate 	cp = rfs4_findclient(&args->client, &create, NULL);
7019*7c478bd9Sstevel@tonic-gate 
7020*7c478bd9Sstevel@tonic-gate 	/* Should never happen */
7021*7c478bd9Sstevel@tonic-gate 	ASSERT(cp != NULL);
7022*7c478bd9Sstevel@tonic-gate 
7023*7c478bd9Sstevel@tonic-gate 	if (cp == NULL) {
7024*7c478bd9Sstevel@tonic-gate 		*cs->statusp = res->status = NFS4ERR_SERVERFAULT;
7025*7c478bd9Sstevel@tonic-gate 		return;
7026*7c478bd9Sstevel@tonic-gate 	}
7027*7c478bd9Sstevel@tonic-gate 
7028*7c478bd9Sstevel@tonic-gate 	/*
7029*7c478bd9Sstevel@tonic-gate 	 * Easiest case. Client identifier is newly created and is
7030*7c478bd9Sstevel@tonic-gate 	 * unconfirmed.  Also note that for this case, no other
7031*7c478bd9Sstevel@tonic-gate 	 * entries exist for the client identifier.  Nothing else to
7032*7c478bd9Sstevel@tonic-gate 	 * check.  Just setup the response and respond.
7033*7c478bd9Sstevel@tonic-gate 	 */
7034*7c478bd9Sstevel@tonic-gate 	if (create) {
7035*7c478bd9Sstevel@tonic-gate 		*cs->statusp = res->status = NFS4_OK;
7036*7c478bd9Sstevel@tonic-gate 		res->SETCLIENTID4res_u.resok4.clientid = cp->clientid;
7037*7c478bd9Sstevel@tonic-gate 		res->SETCLIENTID4res_u.resok4.setclientid_confirm =
7038*7c478bd9Sstevel@tonic-gate 							cp->confirm_verf;
7039*7c478bd9Sstevel@tonic-gate 		/* Setup callback information; CB_NULL confirmation later */
7040*7c478bd9Sstevel@tonic-gate 		rfs4_client_setcb(cp, &args->callback, args->callback_ident);
7041*7c478bd9Sstevel@tonic-gate 
7042*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
7043*7c478bd9Sstevel@tonic-gate 		return;
7044*7c478bd9Sstevel@tonic-gate 	}
7045*7c478bd9Sstevel@tonic-gate 
7046*7c478bd9Sstevel@tonic-gate 	/*
7047*7c478bd9Sstevel@tonic-gate 	 * An existing, confirmed client may exist but it may not have
7048*7c478bd9Sstevel@tonic-gate 	 * been active for at least one lease period.  If so, then
7049*7c478bd9Sstevel@tonic-gate 	 * "close" the client and create a new client identifier
7050*7c478bd9Sstevel@tonic-gate 	 */
7051*7c478bd9Sstevel@tonic-gate 	if (rfs4_lease_expired(cp)) {
7052*7c478bd9Sstevel@tonic-gate 		rfs4_client_close(cp);
7053*7c478bd9Sstevel@tonic-gate 		goto retry;
7054*7c478bd9Sstevel@tonic-gate 	}
7055*7c478bd9Sstevel@tonic-gate 
7056*7c478bd9Sstevel@tonic-gate 	if (cp->need_confirm == TRUE)
7057*7c478bd9Sstevel@tonic-gate 		cp_unconfirmed = cp;
7058*7c478bd9Sstevel@tonic-gate 	else
7059*7c478bd9Sstevel@tonic-gate 		cp_confirmed = cp;
7060*7c478bd9Sstevel@tonic-gate 
7061*7c478bd9Sstevel@tonic-gate 	cp = NULL;
7062*7c478bd9Sstevel@tonic-gate 
7063*7c478bd9Sstevel@tonic-gate 	/*
7064*7c478bd9Sstevel@tonic-gate 	 * We have a confirmed client, now check for an
7065*7c478bd9Sstevel@tonic-gate 	 * unconfimred entry
7066*7c478bd9Sstevel@tonic-gate 	 */
7067*7c478bd9Sstevel@tonic-gate 	if (cp_confirmed) {
7068*7c478bd9Sstevel@tonic-gate 		/* If creds don't match then client identifier is inuse */
7069*7c478bd9Sstevel@tonic-gate 		if (!creds_ok(cp_confirmed->cr_set, req, cs)) {
7070*7c478bd9Sstevel@tonic-gate 			rfs4_cbinfo_t *cbp;
7071*7c478bd9Sstevel@tonic-gate 			/*
7072*7c478bd9Sstevel@tonic-gate 			 * Some one else has established this client
7073*7c478bd9Sstevel@tonic-gate 			 * id. Try and say * who they are. We will use
7074*7c478bd9Sstevel@tonic-gate 			 * the call back address supplied by * the
7075*7c478bd9Sstevel@tonic-gate 			 * first client.
7076*7c478bd9Sstevel@tonic-gate 			 */
7077*7c478bd9Sstevel@tonic-gate 			*cs->statusp = res->status = NFS4ERR_CLID_INUSE;
7078*7c478bd9Sstevel@tonic-gate 
7079*7c478bd9Sstevel@tonic-gate 			addr = netid = NULL;
7080*7c478bd9Sstevel@tonic-gate 
7081*7c478bd9Sstevel@tonic-gate 			cbp = &cp_confirmed->cbinfo;
7082*7c478bd9Sstevel@tonic-gate 			if (cbp->cb_callback.cb_location.r_addr &&
7083*7c478bd9Sstevel@tonic-gate 			    cbp->cb_callback.cb_location.r_netid) {
7084*7c478bd9Sstevel@tonic-gate 				cb_client4 *cbcp = &cbp->cb_callback;
7085*7c478bd9Sstevel@tonic-gate 
7086*7c478bd9Sstevel@tonic-gate 				len = strlen(cbcp->cb_location.r_addr)+1;
7087*7c478bd9Sstevel@tonic-gate 				addr = kmem_alloc(len, KM_SLEEP);
7088*7c478bd9Sstevel@tonic-gate 				bcopy(cbcp->cb_location.r_addr, addr, len);
7089*7c478bd9Sstevel@tonic-gate 				len = strlen(cbcp->cb_location.r_netid)+1;
7090*7c478bd9Sstevel@tonic-gate 				netid = kmem_alloc(len, KM_SLEEP);
7091*7c478bd9Sstevel@tonic-gate 				bcopy(cbcp->cb_location.r_netid, netid, len);
7092*7c478bd9Sstevel@tonic-gate 			}
7093*7c478bd9Sstevel@tonic-gate 
7094*7c478bd9Sstevel@tonic-gate 			res->SETCLIENTID4res_u.client_using.r_addr = addr;
7095*7c478bd9Sstevel@tonic-gate 			res->SETCLIENTID4res_u.client_using.r_netid = netid;
7096*7c478bd9Sstevel@tonic-gate 
7097*7c478bd9Sstevel@tonic-gate 			rfs4_client_rele(cp_confirmed);
7098*7c478bd9Sstevel@tonic-gate 		}
7099*7c478bd9Sstevel@tonic-gate 
7100*7c478bd9Sstevel@tonic-gate 		/*
7101*7c478bd9Sstevel@tonic-gate 		 * Confirmed, creds match, and verifier matches; must
7102*7c478bd9Sstevel@tonic-gate 		 * be an update of the callback info
7103*7c478bd9Sstevel@tonic-gate 		 */
7104*7c478bd9Sstevel@tonic-gate 		if (cp_confirmed->nfs_client.verifier ==
7105*7c478bd9Sstevel@tonic-gate 						args->client.verifier) {
7106*7c478bd9Sstevel@tonic-gate 			/* Setup callback information */
7107*7c478bd9Sstevel@tonic-gate 			rfs4_client_setcb(cp_confirmed, &args->callback,
7108*7c478bd9Sstevel@tonic-gate 						args->callback_ident);
7109*7c478bd9Sstevel@tonic-gate 
7110*7c478bd9Sstevel@tonic-gate 			/* everything okay -- move ahead */
7111*7c478bd9Sstevel@tonic-gate 			*cs->statusp = res->status = NFS4_OK;
7112*7c478bd9Sstevel@tonic-gate 			res->SETCLIENTID4res_u.resok4.clientid =
7113*7c478bd9Sstevel@tonic-gate 				cp_confirmed->clientid;
7114*7c478bd9Sstevel@tonic-gate 
7115*7c478bd9Sstevel@tonic-gate 			/* update the confirm_verifier and return it */
7116*7c478bd9Sstevel@tonic-gate 			rfs4_client_scv_next(cp_confirmed);
7117*7c478bd9Sstevel@tonic-gate 			res->SETCLIENTID4res_u.resok4.setclientid_confirm =
7118*7c478bd9Sstevel@tonic-gate 						cp_confirmed->confirm_verf;
7119*7c478bd9Sstevel@tonic-gate 
7120*7c478bd9Sstevel@tonic-gate 			rfs4_client_rele(cp_confirmed);
7121*7c478bd9Sstevel@tonic-gate 			return;
7122*7c478bd9Sstevel@tonic-gate 		}
7123*7c478bd9Sstevel@tonic-gate 
7124*7c478bd9Sstevel@tonic-gate 		/*
7125*7c478bd9Sstevel@tonic-gate 		 * Creds match but the verifier doesn't.  Must search
7126*7c478bd9Sstevel@tonic-gate 		 * for an unconfirmed client that would be replaced by
7127*7c478bd9Sstevel@tonic-gate 		 * this request.
7128*7c478bd9Sstevel@tonic-gate 		 */
7129*7c478bd9Sstevel@tonic-gate 		create = FALSE;
7130*7c478bd9Sstevel@tonic-gate 		cp_unconfirmed = rfs4_findclient(&args->client, &create,
7131*7c478bd9Sstevel@tonic-gate 						cp_confirmed);
7132*7c478bd9Sstevel@tonic-gate 	}
7133*7c478bd9Sstevel@tonic-gate 
7134*7c478bd9Sstevel@tonic-gate 	/*
7135*7c478bd9Sstevel@tonic-gate 	 * At this point, we have taken care of the brand new client
7136*7c478bd9Sstevel@tonic-gate 	 * struct, INUSE case, update of an existing, and confirmed
7137*7c478bd9Sstevel@tonic-gate 	 * client struct.
7138*7c478bd9Sstevel@tonic-gate 	 */
7139*7c478bd9Sstevel@tonic-gate 
7140*7c478bd9Sstevel@tonic-gate 	/*
7141*7c478bd9Sstevel@tonic-gate 	 * check to see if things have changed while we originally
7142*7c478bd9Sstevel@tonic-gate 	 * picked up the client struct.  If they have, then return and
7143*7c478bd9Sstevel@tonic-gate 	 * retry the processing of this SETCLIENTID request.
7144*7c478bd9Sstevel@tonic-gate 	 */
7145*7c478bd9Sstevel@tonic-gate 	if (cp_unconfirmed) {
7146*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(cp_unconfirmed->dbe);
7147*7c478bd9Sstevel@tonic-gate 		if (!cp_unconfirmed->need_confirm) {
7148*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_unlock(cp_unconfirmed->dbe);
7149*7c478bd9Sstevel@tonic-gate 			rfs4_client_rele(cp_unconfirmed);
7150*7c478bd9Sstevel@tonic-gate 			if (cp_confirmed)
7151*7c478bd9Sstevel@tonic-gate 				rfs4_client_rele(cp_confirmed);
7152*7c478bd9Sstevel@tonic-gate 			goto retry;
7153*7c478bd9Sstevel@tonic-gate 		}
7154*7c478bd9Sstevel@tonic-gate 		/* do away with the old unconfirmed one */
7155*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_invalidate(cp_unconfirmed->dbe);
7156*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(cp_unconfirmed->dbe);
7157*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp_unconfirmed);
7158*7c478bd9Sstevel@tonic-gate 		cp_unconfirmed = NULL;
7159*7c478bd9Sstevel@tonic-gate 	}
7160*7c478bd9Sstevel@tonic-gate 
7161*7c478bd9Sstevel@tonic-gate 	/*
7162*7c478bd9Sstevel@tonic-gate 	 * This search will temporarily hide the confirmed client
7163*7c478bd9Sstevel@tonic-gate 	 * struct while a new client struct is created as the
7164*7c478bd9Sstevel@tonic-gate 	 * unconfirmed one.
7165*7c478bd9Sstevel@tonic-gate 	 */
7166*7c478bd9Sstevel@tonic-gate 	create = TRUE;
7167*7c478bd9Sstevel@tonic-gate 	newcp = rfs4_findclient(&args->client, &create, cp_confirmed);
7168*7c478bd9Sstevel@tonic-gate 
7169*7c478bd9Sstevel@tonic-gate 	ASSERT(newcp != NULL);
7170*7c478bd9Sstevel@tonic-gate 
7171*7c478bd9Sstevel@tonic-gate 	if (newcp == NULL) {
7172*7c478bd9Sstevel@tonic-gate 		*cs->statusp = res->status = NFS4ERR_SERVERFAULT;
7173*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp_confirmed);
7174*7c478bd9Sstevel@tonic-gate 		return;
7175*7c478bd9Sstevel@tonic-gate 	}
7176*7c478bd9Sstevel@tonic-gate 
7177*7c478bd9Sstevel@tonic-gate 	/*
7178*7c478bd9Sstevel@tonic-gate 	 * If one was not created, then a similar request must be in
7179*7c478bd9Sstevel@tonic-gate 	 * process so release and start over with this one
7180*7c478bd9Sstevel@tonic-gate 	 */
7181*7c478bd9Sstevel@tonic-gate 	if (create != TRUE) {
7182*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(newcp);
7183*7c478bd9Sstevel@tonic-gate 		if (cp_confirmed)
7184*7c478bd9Sstevel@tonic-gate 			rfs4_client_rele(cp_confirmed);
7185*7c478bd9Sstevel@tonic-gate 		goto retry;
7186*7c478bd9Sstevel@tonic-gate 	}
7187*7c478bd9Sstevel@tonic-gate 
7188*7c478bd9Sstevel@tonic-gate 	*cs->statusp = res->status = NFS4_OK;
7189*7c478bd9Sstevel@tonic-gate 	res->SETCLIENTID4res_u.resok4.clientid = newcp->clientid;
7190*7c478bd9Sstevel@tonic-gate 	res->SETCLIENTID4res_u.resok4.setclientid_confirm =
7191*7c478bd9Sstevel@tonic-gate 							newcp->confirm_verf;
7192*7c478bd9Sstevel@tonic-gate 	/* Setup callback information; CB_NULL confirmation later */
7193*7c478bd9Sstevel@tonic-gate 	rfs4_client_setcb(newcp, &args->callback,
7194*7c478bd9Sstevel@tonic-gate 				args->callback_ident);
7195*7c478bd9Sstevel@tonic-gate 
7196*7c478bd9Sstevel@tonic-gate 	newcp->cp_confirmed = cp_confirmed;
7197*7c478bd9Sstevel@tonic-gate 
7198*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(newcp);
7199*7c478bd9Sstevel@tonic-gate }
7200*7c478bd9Sstevel@tonic-gate 
7201*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7202*7c478bd9Sstevel@tonic-gate void
7203*7c478bd9Sstevel@tonic-gate rfs4_op_setclientid_confirm(nfs_argop4 *argop, nfs_resop4 *resop,
7204*7c478bd9Sstevel@tonic-gate 			    struct svc_req *req, struct compound_state *cs)
7205*7c478bd9Sstevel@tonic-gate {
7206*7c478bd9Sstevel@tonic-gate 	SETCLIENTID_CONFIRM4args *args =
7207*7c478bd9Sstevel@tonic-gate 		&argop->nfs_argop4_u.opsetclientid_confirm;
7208*7c478bd9Sstevel@tonic-gate 	SETCLIENTID_CONFIRM4res *res =
7209*7c478bd9Sstevel@tonic-gate 		&resop->nfs_resop4_u.opsetclientid_confirm;
7210*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp, *cptoclose = NULL;
7211*7c478bd9Sstevel@tonic-gate 
7212*7c478bd9Sstevel@tonic-gate 	*cs->statusp = res->status = NFS4_OK;
7213*7c478bd9Sstevel@tonic-gate 
7214*7c478bd9Sstevel@tonic-gate 	cp = rfs4_findclient_by_id(args->clientid, TRUE);
7215*7c478bd9Sstevel@tonic-gate 
7216*7c478bd9Sstevel@tonic-gate 	if (cp == NULL) {
7217*7c478bd9Sstevel@tonic-gate 		*cs->statusp = res->status =
7218*7c478bd9Sstevel@tonic-gate 			rfs4_check_clientid(&args->clientid, 1);
7219*7c478bd9Sstevel@tonic-gate 		return;
7220*7c478bd9Sstevel@tonic-gate 	}
7221*7c478bd9Sstevel@tonic-gate 
7222*7c478bd9Sstevel@tonic-gate 	if (!creds_ok(cp, req, cs)) {
7223*7c478bd9Sstevel@tonic-gate 		*cs->statusp = res->status = NFS4ERR_CLID_INUSE;
7224*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
7225*7c478bd9Sstevel@tonic-gate 		return;
7226*7c478bd9Sstevel@tonic-gate 	}
7227*7c478bd9Sstevel@tonic-gate 
7228*7c478bd9Sstevel@tonic-gate 	/* If the verifier doesn't match, the record doesn't match */
7229*7c478bd9Sstevel@tonic-gate 	if (cp->confirm_verf != args->setclientid_confirm) {
7230*7c478bd9Sstevel@tonic-gate 		*cs->statusp = res->status = NFS4ERR_STALE_CLIENTID;
7231*7c478bd9Sstevel@tonic-gate 		rfs4_client_rele(cp);
7232*7c478bd9Sstevel@tonic-gate 		return;
7233*7c478bd9Sstevel@tonic-gate 	}
7234*7c478bd9Sstevel@tonic-gate 
7235*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(cp->dbe);
7236*7c478bd9Sstevel@tonic-gate 	cp->need_confirm = FALSE;
7237*7c478bd9Sstevel@tonic-gate 	if (cp->cp_confirmed) {
7238*7c478bd9Sstevel@tonic-gate 		cptoclose = cp->cp_confirmed;
7239*7c478bd9Sstevel@tonic-gate 		cptoclose->ss_remove = 1;
7240*7c478bd9Sstevel@tonic-gate 		cp->cp_confirmed = NULL;
7241*7c478bd9Sstevel@tonic-gate 	}
7242*7c478bd9Sstevel@tonic-gate 
7243*7c478bd9Sstevel@tonic-gate 	/*
7244*7c478bd9Sstevel@tonic-gate 	 * Record clientid in stable storage
7245*7c478bd9Sstevel@tonic-gate 	 */
7246*7c478bd9Sstevel@tonic-gate 	rfs4_ss_clid(cp, req);
7247*7c478bd9Sstevel@tonic-gate 
7248*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(cp->dbe);
7249*7c478bd9Sstevel@tonic-gate 
7250*7c478bd9Sstevel@tonic-gate 	if (cptoclose)
7251*7c478bd9Sstevel@tonic-gate 		/* don't need to rele, client_close does it */
7252*7c478bd9Sstevel@tonic-gate 		rfs4_client_close(cptoclose);
7253*7c478bd9Sstevel@tonic-gate 
7254*7c478bd9Sstevel@tonic-gate 	/* If needed, initiate CB_NULL call for callback path */
7255*7c478bd9Sstevel@tonic-gate 	rfs4_deleg_cb_check(cp);
7256*7c478bd9Sstevel@tonic-gate 	rfs4_update_lease(cp);
7257*7c478bd9Sstevel@tonic-gate 
7258*7c478bd9Sstevel@tonic-gate 	/*
7259*7c478bd9Sstevel@tonic-gate 	 * Update the client's associated server instance, if it's changed
7260*7c478bd9Sstevel@tonic-gate 	 * since the client was created.
7261*7c478bd9Sstevel@tonic-gate 	 */
7262*7c478bd9Sstevel@tonic-gate 	if (rfs4_servinst(cp) != rfs4_cur_servinst)
7263*7c478bd9Sstevel@tonic-gate 		rfs4_servinst_assign(cp, rfs4_cur_servinst);
7264*7c478bd9Sstevel@tonic-gate 
7265*7c478bd9Sstevel@tonic-gate 	/*
7266*7c478bd9Sstevel@tonic-gate 	 * Check to see if client can perform reclaims
7267*7c478bd9Sstevel@tonic-gate 	 */
7268*7c478bd9Sstevel@tonic-gate 	rfs4_ss_chkclid(cp);
7269*7c478bd9Sstevel@tonic-gate 
7270*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(cp);
7271*7c478bd9Sstevel@tonic-gate }
7272*7c478bd9Sstevel@tonic-gate 
7273*7c478bd9Sstevel@tonic-gate 
7274*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7275*7c478bd9Sstevel@tonic-gate void
7276*7c478bd9Sstevel@tonic-gate rfs4_op_close(nfs_argop4 *argop, nfs_resop4 *resop,
7277*7c478bd9Sstevel@tonic-gate 	    struct svc_req *req, struct compound_state *cs)
7278*7c478bd9Sstevel@tonic-gate {
7279*7c478bd9Sstevel@tonic-gate 	/* XXX Currently not using req arg */
7280*7c478bd9Sstevel@tonic-gate 	CLOSE4args *args = &argop->nfs_argop4_u.opclose;
7281*7c478bd9Sstevel@tonic-gate 	CLOSE4res *resp = &resop->nfs_resop4_u.opclose;
7282*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *sp;
7283*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
7284*7c478bd9Sstevel@tonic-gate 
7285*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
7286*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
7287*7c478bd9Sstevel@tonic-gate 		return;
7288*7c478bd9Sstevel@tonic-gate 	}
7289*7c478bd9Sstevel@tonic-gate 
7290*7c478bd9Sstevel@tonic-gate 	status = rfs4_get_state(&args->open_stateid, &sp, RFS4_DBS_INVALID);
7291*7c478bd9Sstevel@tonic-gate 	if (status != NFS4_OK) {
7292*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = status;
7293*7c478bd9Sstevel@tonic-gate 		return;
7294*7c478bd9Sstevel@tonic-gate 	}
7295*7c478bd9Sstevel@tonic-gate 
7296*7c478bd9Sstevel@tonic-gate 	/* Ensure specified filehandle matches */
7297*7c478bd9Sstevel@tonic-gate 	if (cs->vp != sp->finfo->vp) {
7298*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(sp);
7299*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7300*7c478bd9Sstevel@tonic-gate 		return;
7301*7c478bd9Sstevel@tonic-gate 	}
7302*7c478bd9Sstevel@tonic-gate 
7303*7c478bd9Sstevel@tonic-gate 	/* hold off other access to open_owner while we tinker */
7304*7c478bd9Sstevel@tonic-gate 	rfs4_sw_enter(&sp->owner->oo_sw);
7305*7c478bd9Sstevel@tonic-gate 
7306*7c478bd9Sstevel@tonic-gate 	switch (rfs4_check_stateid_seqid(sp, &args->open_stateid)) {
7307*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OKAY:
7308*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_open_seqid(args->seqid, sp->owner,
7309*7c478bd9Sstevel@tonic-gate 			resop) != NFS4_CHKSEQ_OKAY) {
7310*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
7311*7c478bd9Sstevel@tonic-gate 			goto end;
7312*7c478bd9Sstevel@tonic-gate 		}
7313*7c478bd9Sstevel@tonic-gate 		break;
7314*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OLD:
7315*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
7316*7c478bd9Sstevel@tonic-gate 		goto end;
7317*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_BAD:
7318*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7319*7c478bd9Sstevel@tonic-gate 		goto end;
7320*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_EXPIRED:
7321*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
7322*7c478bd9Sstevel@tonic-gate 		goto end;
7323*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_CLOSED:
7324*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
7325*7c478bd9Sstevel@tonic-gate 		goto end;
7326*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_UNCONFIRMED:
7327*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7328*7c478bd9Sstevel@tonic-gate 		goto end;
7329*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_REPLAY:
7330*7c478bd9Sstevel@tonic-gate 		/* Check the sequence id for the open owner */
7331*7c478bd9Sstevel@tonic-gate 		switch (rfs4_check_open_seqid(args->seqid, sp->owner, resop)) {
7332*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_OKAY:
7333*7c478bd9Sstevel@tonic-gate 			/*
7334*7c478bd9Sstevel@tonic-gate 			 * This is replayed stateid; if seqid matches
7335*7c478bd9Sstevel@tonic-gate 			 * next expected, then client is using wrong seqid.
7336*7c478bd9Sstevel@tonic-gate 			 */
7337*7c478bd9Sstevel@tonic-gate 			/* FALL THROUGH */
7338*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_BAD:
7339*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
7340*7c478bd9Sstevel@tonic-gate 			goto end;
7341*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_REPLAY:
7342*7c478bd9Sstevel@tonic-gate 			/*
7343*7c478bd9Sstevel@tonic-gate 			 * Note this case is the duplicate case so
7344*7c478bd9Sstevel@tonic-gate 			 * resp->status is already set.
7345*7c478bd9Sstevel@tonic-gate 			 */
7346*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status;
7347*7c478bd9Sstevel@tonic-gate 			rfs4_update_lease(sp->owner->client);
7348*7c478bd9Sstevel@tonic-gate 			goto end;
7349*7c478bd9Sstevel@tonic-gate 		}
7350*7c478bd9Sstevel@tonic-gate 		break;
7351*7c478bd9Sstevel@tonic-gate 	default:
7352*7c478bd9Sstevel@tonic-gate 		ASSERT(FALSE);
7353*7c478bd9Sstevel@tonic-gate 		break;
7354*7c478bd9Sstevel@tonic-gate 	}
7355*7c478bd9Sstevel@tonic-gate 
7356*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(sp->dbe);
7357*7c478bd9Sstevel@tonic-gate 
7358*7c478bd9Sstevel@tonic-gate 	/* Update the stateid. */
7359*7c478bd9Sstevel@tonic-gate 	next_stateid(&sp->stateid);
7360*7c478bd9Sstevel@tonic-gate 	resp->open_stateid = sp->stateid.stateid;
7361*7c478bd9Sstevel@tonic-gate 
7362*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(sp->dbe);
7363*7c478bd9Sstevel@tonic-gate 
7364*7c478bd9Sstevel@tonic-gate 	rfs4_update_lease(sp->owner->client);
7365*7c478bd9Sstevel@tonic-gate 	rfs4_update_open_sequence(sp->owner);
7366*7c478bd9Sstevel@tonic-gate 	rfs4_update_open_resp(sp->owner, resop, NULL);
7367*7c478bd9Sstevel@tonic-gate 
7368*7c478bd9Sstevel@tonic-gate 	rfs4_state_close(sp, FALSE, FALSE, cs->cr);
7369*7c478bd9Sstevel@tonic-gate 
7370*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = status;
7371*7c478bd9Sstevel@tonic-gate 
7372*7c478bd9Sstevel@tonic-gate end:
7373*7c478bd9Sstevel@tonic-gate 	rfs4_sw_exit(&sp->owner->oo_sw);
7374*7c478bd9Sstevel@tonic-gate 	rfs4_state_rele(sp);
7375*7c478bd9Sstevel@tonic-gate }
7376*7c478bd9Sstevel@tonic-gate 
7377*7c478bd9Sstevel@tonic-gate /*
7378*7c478bd9Sstevel@tonic-gate  * Manage the counts on the file struct and close all file locks
7379*7c478bd9Sstevel@tonic-gate  */
7380*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7381*7c478bd9Sstevel@tonic-gate void
7382*7c478bd9Sstevel@tonic-gate rfs4_release_share_lock_state(rfs4_state_t *sp, cred_t *cr,
7383*7c478bd9Sstevel@tonic-gate 	bool_t close_of_client)
7384*7c478bd9Sstevel@tonic-gate {
7385*7c478bd9Sstevel@tonic-gate 	rfs4_file_t *fp = sp->finfo;
7386*7c478bd9Sstevel@tonic-gate 	rfs4_lo_state_t *lsp;
7387*7c478bd9Sstevel@tonic-gate 	struct shrlock shr;
7388*7c478bd9Sstevel@tonic-gate 	struct shr_locowner shr_loco;
7389*7c478bd9Sstevel@tonic-gate 	int fflags, s_access, s_deny;
7390*7c478bd9Sstevel@tonic-gate 
7391*7c478bd9Sstevel@tonic-gate 	fflags = s_access = s_deny = 0;
7392*7c478bd9Sstevel@tonic-gate 	/*
7393*7c478bd9Sstevel@tonic-gate 	 * Decrement the count for each access and deny bit that this
7394*7c478bd9Sstevel@tonic-gate 	 * state has contributed to the file. If the file counts go to zero
7395*7c478bd9Sstevel@tonic-gate 	 * clear the appropriate bit in the appropriate mask.
7396*7c478bd9Sstevel@tonic-gate 	 */
7397*7c478bd9Sstevel@tonic-gate 
7398*7c478bd9Sstevel@tonic-gate 	if (sp->share_access & OPEN4_SHARE_ACCESS_READ) {
7399*7c478bd9Sstevel@tonic-gate 		fp->access_read--;
7400*7c478bd9Sstevel@tonic-gate 		fflags |= FREAD;
7401*7c478bd9Sstevel@tonic-gate 		s_access |= F_RDACC;
7402*7c478bd9Sstevel@tonic-gate 		if (fp->access_read == 0)
7403*7c478bd9Sstevel@tonic-gate 			fp->share_access &= ~OPEN4_SHARE_ACCESS_READ;
7404*7c478bd9Sstevel@tonic-gate 	}
7405*7c478bd9Sstevel@tonic-gate 	if (sp->share_access & OPEN4_SHARE_ACCESS_WRITE) {
7406*7c478bd9Sstevel@tonic-gate 		fp->access_write--;
7407*7c478bd9Sstevel@tonic-gate 		fflags |= FWRITE;
7408*7c478bd9Sstevel@tonic-gate 		s_access |= F_WRACC;
7409*7c478bd9Sstevel@tonic-gate 		if (fp->access_write == 0)
7410*7c478bd9Sstevel@tonic-gate 			fp->share_access &= ~OPEN4_SHARE_ACCESS_WRITE;
7411*7c478bd9Sstevel@tonic-gate 	}
7412*7c478bd9Sstevel@tonic-gate 	if (sp->share_deny & OPEN4_SHARE_DENY_READ) {
7413*7c478bd9Sstevel@tonic-gate 		fp->deny_read--;
7414*7c478bd9Sstevel@tonic-gate 		s_deny |= F_RDDNY;
7415*7c478bd9Sstevel@tonic-gate 		if (fp->deny_read == 0)
7416*7c478bd9Sstevel@tonic-gate 			fp->share_deny &= ~OPEN4_SHARE_DENY_READ;
7417*7c478bd9Sstevel@tonic-gate 	}
7418*7c478bd9Sstevel@tonic-gate 	if (sp->share_deny & OPEN4_SHARE_DENY_WRITE) {
7419*7c478bd9Sstevel@tonic-gate 		fp->deny_write--;
7420*7c478bd9Sstevel@tonic-gate 		s_deny |= F_WRDNY;
7421*7c478bd9Sstevel@tonic-gate 		if (fp->deny_write == 0)
7422*7c478bd9Sstevel@tonic-gate 			fp->share_deny &= ~OPEN4_SHARE_DENY_WRITE;
7423*7c478bd9Sstevel@tonic-gate 	}
7424*7c478bd9Sstevel@tonic-gate 
7425*7c478bd9Sstevel@tonic-gate 	/*
7426*7c478bd9Sstevel@tonic-gate 	 * If this call is part of the larger closing down of client
7427*7c478bd9Sstevel@tonic-gate 	 * state then it is just easier to release all locks
7428*7c478bd9Sstevel@tonic-gate 	 * associated with this client instead of going through each
7429*7c478bd9Sstevel@tonic-gate 	 * individual file and cleaning locks there.
7430*7c478bd9Sstevel@tonic-gate 	 */
7431*7c478bd9Sstevel@tonic-gate 	if (close_of_client) {
7432*7c478bd9Sstevel@tonic-gate 		if (sp->owner->client->unlksys_completed == FALSE &&
7433*7c478bd9Sstevel@tonic-gate 		    sp->lockownerlist.next->lsp != NULL &&
7434*7c478bd9Sstevel@tonic-gate 			sp->owner->client->sysidt != LM_NOSYSID) {
7435*7c478bd9Sstevel@tonic-gate 			/* Is the PxFS kernel module loaded? */
7436*7c478bd9Sstevel@tonic-gate 			if (lm_remove_file_locks != NULL) {
7437*7c478bd9Sstevel@tonic-gate 				int new_sysid;
7438*7c478bd9Sstevel@tonic-gate 
7439*7c478bd9Sstevel@tonic-gate 				/* Encode the cluster nodeid in new sysid */
7440*7c478bd9Sstevel@tonic-gate 				new_sysid = sp->owner->client->sysidt;
7441*7c478bd9Sstevel@tonic-gate 				lm_set_nlmid_flk(&new_sysid);
7442*7c478bd9Sstevel@tonic-gate 
7443*7c478bd9Sstevel@tonic-gate 				/*
7444*7c478bd9Sstevel@tonic-gate 				 * This PxFS routine removes file locks for a
7445*7c478bd9Sstevel@tonic-gate 				 * client over all nodes of a cluster.
7446*7c478bd9Sstevel@tonic-gate 				 */
7447*7c478bd9Sstevel@tonic-gate 				NFS4_DEBUG(rfs4_debug, (CE_NOTE,
7448*7c478bd9Sstevel@tonic-gate 				    "lm_remove_file_locks(sysid=0x%x)\n",
7449*7c478bd9Sstevel@tonic-gate 				    new_sysid));
7450*7c478bd9Sstevel@tonic-gate 				(*lm_remove_file_locks)(new_sysid);
7451*7c478bd9Sstevel@tonic-gate 			} else {
7452*7c478bd9Sstevel@tonic-gate 				struct flock64 flk;
7453*7c478bd9Sstevel@tonic-gate 
7454*7c478bd9Sstevel@tonic-gate 				/* Release all locks for this client */
7455*7c478bd9Sstevel@tonic-gate 				flk.l_type = F_UNLKSYS;
7456*7c478bd9Sstevel@tonic-gate 				flk.l_whence = 0;
7457*7c478bd9Sstevel@tonic-gate 				flk.l_start = 0;
7458*7c478bd9Sstevel@tonic-gate 				flk.l_len = 0;
7459*7c478bd9Sstevel@tonic-gate 				flk.l_sysid = sp->owner->client->sysidt;
7460*7c478bd9Sstevel@tonic-gate 				flk.l_pid = 0;
7461*7c478bd9Sstevel@tonic-gate 				(void) VOP_FRLOCK(sp->finfo->vp, F_SETLK, &flk,
7462*7c478bd9Sstevel@tonic-gate 				    F_REMOTELOCK | FREAD | FWRITE,
7463*7c478bd9Sstevel@tonic-gate 				    (u_offset_t)0, NULL, CRED());
7464*7c478bd9Sstevel@tonic-gate 			}
7465*7c478bd9Sstevel@tonic-gate 
7466*7c478bd9Sstevel@tonic-gate 			sp->owner->client->unlksys_completed = TRUE;
7467*7c478bd9Sstevel@tonic-gate 		}
7468*7c478bd9Sstevel@tonic-gate 	}
7469*7c478bd9Sstevel@tonic-gate 
7470*7c478bd9Sstevel@tonic-gate 	/*
7471*7c478bd9Sstevel@tonic-gate 	 * Release all locks on this file by this lock owner or at
7472*7c478bd9Sstevel@tonic-gate 	 * least mark the locks as having been released
7473*7c478bd9Sstevel@tonic-gate 	 */
7474*7c478bd9Sstevel@tonic-gate 	for (lsp = sp->lockownerlist.next->lsp; lsp != NULL;
7475*7c478bd9Sstevel@tonic-gate 		lsp = lsp->lockownerlist.next->lsp) {
7476*7c478bd9Sstevel@tonic-gate 
7477*7c478bd9Sstevel@tonic-gate 		lsp->locks_cleaned = TRUE;
7478*7c478bd9Sstevel@tonic-gate 
7479*7c478bd9Sstevel@tonic-gate 		/* Was this already taken care of above? */
7480*7c478bd9Sstevel@tonic-gate 		if (!close_of_client &&
7481*7c478bd9Sstevel@tonic-gate 		    sp->owner->client->sysidt != LM_NOSYSID)
7482*7c478bd9Sstevel@tonic-gate 			(void) cleanlocks(sp->finfo->vp, lsp->locker->pid,
7483*7c478bd9Sstevel@tonic-gate 				lsp->locker->client->sysidt);
7484*7c478bd9Sstevel@tonic-gate 	}
7485*7c478bd9Sstevel@tonic-gate 
7486*7c478bd9Sstevel@tonic-gate 	/*
7487*7c478bd9Sstevel@tonic-gate 	 * Release any shrlocks associated with this open state ID.
7488*7c478bd9Sstevel@tonic-gate 	 * This must be done before the rfs4_state gets marked closed.
7489*7c478bd9Sstevel@tonic-gate 	 */
7490*7c478bd9Sstevel@tonic-gate 	if (sp->owner->client->sysidt != LM_NOSYSID) {
7491*7c478bd9Sstevel@tonic-gate 		shr.s_access = s_access;
7492*7c478bd9Sstevel@tonic-gate 		shr.s_deny = s_deny;
7493*7c478bd9Sstevel@tonic-gate 		shr.s_pid = rfs4_dbe_getid(sp->owner->dbe);
7494*7c478bd9Sstevel@tonic-gate 		shr.s_sysid = sp->owner->client->sysidt;
7495*7c478bd9Sstevel@tonic-gate 		shr_loco.sl_pid = shr.s_pid;
7496*7c478bd9Sstevel@tonic-gate 		shr_loco.sl_id = shr.s_sysid;
7497*7c478bd9Sstevel@tonic-gate 		shr.s_owner = (caddr_t)&shr_loco;
7498*7c478bd9Sstevel@tonic-gate 		shr.s_own_len = sizeof (shr_loco);
7499*7c478bd9Sstevel@tonic-gate 		(void) vop_shrlock(sp->finfo->vp, F_UNSHARE, &shr, fflags);
7500*7c478bd9Sstevel@tonic-gate 	}
7501*7c478bd9Sstevel@tonic-gate }
7502*7c478bd9Sstevel@tonic-gate 
7503*7c478bd9Sstevel@tonic-gate /*
7504*7c478bd9Sstevel@tonic-gate  * lock_denied: Fill in a LOCK4deneid structure given an flock64 structure.
7505*7c478bd9Sstevel@tonic-gate  */
7506*7c478bd9Sstevel@tonic-gate static nfsstat4
7507*7c478bd9Sstevel@tonic-gate lock_denied(LOCK4denied *dp, struct flock64 *flk)
7508*7c478bd9Sstevel@tonic-gate {
7509*7c478bd9Sstevel@tonic-gate 	rfs4_lockowner_t *lo;
7510*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp;
7511*7c478bd9Sstevel@tonic-gate 	uint32_t len;
7512*7c478bd9Sstevel@tonic-gate 
7513*7c478bd9Sstevel@tonic-gate 	lo = rfs4_findlockowner_by_pid(flk->l_pid);
7514*7c478bd9Sstevel@tonic-gate 	if (lo != NULL) {
7515*7c478bd9Sstevel@tonic-gate 		cp = lo->client;
7516*7c478bd9Sstevel@tonic-gate 		if (rfs4_lease_expired(cp)) {
7517*7c478bd9Sstevel@tonic-gate 			rfs4_lockowner_rele(lo);
7518*7c478bd9Sstevel@tonic-gate 			rfs4_dbe_hold(cp->dbe);
7519*7c478bd9Sstevel@tonic-gate 			rfs4_client_close(cp);
7520*7c478bd9Sstevel@tonic-gate 			return (NFS4ERR_EXPIRED);
7521*7c478bd9Sstevel@tonic-gate 		}
7522*7c478bd9Sstevel@tonic-gate 		dp->owner.clientid = lo->owner.clientid;
7523*7c478bd9Sstevel@tonic-gate 		len = lo->owner.owner_len;
7524*7c478bd9Sstevel@tonic-gate 		dp->owner.owner_val = kmem_alloc(len, KM_SLEEP);
7525*7c478bd9Sstevel@tonic-gate 		bcopy(lo->owner.owner_val, dp->owner.owner_val, len);
7526*7c478bd9Sstevel@tonic-gate 		dp->owner.owner_len = len;
7527*7c478bd9Sstevel@tonic-gate 		rfs4_lockowner_rele(lo);
7528*7c478bd9Sstevel@tonic-gate 		goto finish;
7529*7c478bd9Sstevel@tonic-gate 	}
7530*7c478bd9Sstevel@tonic-gate 
7531*7c478bd9Sstevel@tonic-gate 	/*
7532*7c478bd9Sstevel@tonic-gate 	 * Its not a NFS4 lock. We take advantage that the upper 32 bits
7533*7c478bd9Sstevel@tonic-gate 	 * of the client id contain the boot time for a NFS4 lock. So we
7534*7c478bd9Sstevel@tonic-gate 	 * fabricate and identity by setting clientid to the sysid, and
7535*7c478bd9Sstevel@tonic-gate 	 * the lock owner to the pid.
7536*7c478bd9Sstevel@tonic-gate 	 */
7537*7c478bd9Sstevel@tonic-gate 	dp->owner.clientid = flk->l_sysid;
7538*7c478bd9Sstevel@tonic-gate 	len = sizeof (pid_t);
7539*7c478bd9Sstevel@tonic-gate 	dp->owner.owner_len = len;
7540*7c478bd9Sstevel@tonic-gate 	dp->owner.owner_val = kmem_alloc(len, KM_SLEEP);
7541*7c478bd9Sstevel@tonic-gate 	bcopy(&flk->l_pid, dp->owner.owner_val, len);
7542*7c478bd9Sstevel@tonic-gate finish:
7543*7c478bd9Sstevel@tonic-gate 	dp->offset = flk->l_start;
7544*7c478bd9Sstevel@tonic-gate 	dp->length = flk->l_len;
7545*7c478bd9Sstevel@tonic-gate 
7546*7c478bd9Sstevel@tonic-gate 	if (flk->l_type == F_RDLCK)
7547*7c478bd9Sstevel@tonic-gate 		dp->locktype = READ_LT;
7548*7c478bd9Sstevel@tonic-gate 	else if (flk->l_type == F_WRLCK)
7549*7c478bd9Sstevel@tonic-gate 		dp->locktype = WRITE_LT;
7550*7c478bd9Sstevel@tonic-gate 	else
7551*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);	/* no mapping from POSIX ltype to v4 */
7552*7c478bd9Sstevel@tonic-gate 
7553*7c478bd9Sstevel@tonic-gate 	return (NFS4_OK);
7554*7c478bd9Sstevel@tonic-gate }
7555*7c478bd9Sstevel@tonic-gate 
7556*7c478bd9Sstevel@tonic-gate static int
7557*7c478bd9Sstevel@tonic-gate setlock(vnode_t *vp, struct flock64 *flock, int flag, cred_t *cred)
7558*7c478bd9Sstevel@tonic-gate {
7559*7c478bd9Sstevel@tonic-gate 	int error;
7560*7c478bd9Sstevel@tonic-gate 	struct flock64 flk;
7561*7c478bd9Sstevel@tonic-gate 	int i;
7562*7c478bd9Sstevel@tonic-gate 	clock_t delaytime;
7563*7c478bd9Sstevel@tonic-gate 
7564*7c478bd9Sstevel@tonic-gate retry:
7565*7c478bd9Sstevel@tonic-gate 	delaytime = MSEC_TO_TICK_ROUNDUP(rfs4_lock_delay);
7566*7c478bd9Sstevel@tonic-gate 
7567*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < rfs4_maxlock_tries; i++) {
7568*7c478bd9Sstevel@tonic-gate 		LOCK_PRINT(rfs4_debug, "setlock", F_SETLK, flock);
7569*7c478bd9Sstevel@tonic-gate 		error = VOP_FRLOCK(vp, F_SETLK,
7570*7c478bd9Sstevel@tonic-gate 				flock, flag, (u_offset_t)0, NULL, cred);
7571*7c478bd9Sstevel@tonic-gate 
7572*7c478bd9Sstevel@tonic-gate 		if (error != EAGAIN && error != EACCES)
7573*7c478bd9Sstevel@tonic-gate 			break;
7574*7c478bd9Sstevel@tonic-gate 
7575*7c478bd9Sstevel@tonic-gate 		if (i < rfs4_maxlock_tries - 1) {
7576*7c478bd9Sstevel@tonic-gate 			delay(delaytime);
7577*7c478bd9Sstevel@tonic-gate 			delaytime *= 2;
7578*7c478bd9Sstevel@tonic-gate 		}
7579*7c478bd9Sstevel@tonic-gate 	}
7580*7c478bd9Sstevel@tonic-gate 
7581*7c478bd9Sstevel@tonic-gate 	if (error == EAGAIN || error == EACCES) {
7582*7c478bd9Sstevel@tonic-gate 		/* Get the owner of the lock */
7583*7c478bd9Sstevel@tonic-gate 		flk = *flock;
7584*7c478bd9Sstevel@tonic-gate 		LOCK_PRINT(rfs4_debug, "setlock", F_GETLK, &flk);
7585*7c478bd9Sstevel@tonic-gate 		if (VOP_FRLOCK(vp, F_GETLK,
7586*7c478bd9Sstevel@tonic-gate 			    &flk,  flag, (u_offset_t)0, NULL, cred) == 0) {
7587*7c478bd9Sstevel@tonic-gate 			if (flk.l_type == F_UNLCK) {
7588*7c478bd9Sstevel@tonic-gate 				/* No longer locked, retry */
7589*7c478bd9Sstevel@tonic-gate 				goto retry;
7590*7c478bd9Sstevel@tonic-gate 			}
7591*7c478bd9Sstevel@tonic-gate 			*flock = flk;
7592*7c478bd9Sstevel@tonic-gate 			LOCK_PRINT(rfs4_debug, "setlock(blocking lock)",
7593*7c478bd9Sstevel@tonic-gate 				F_GETLK, &flk);
7594*7c478bd9Sstevel@tonic-gate 		}
7595*7c478bd9Sstevel@tonic-gate 	}
7596*7c478bd9Sstevel@tonic-gate 
7597*7c478bd9Sstevel@tonic-gate 	return (error);
7598*7c478bd9Sstevel@tonic-gate }
7599*7c478bd9Sstevel@tonic-gate 
7600*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7601*7c478bd9Sstevel@tonic-gate static nfsstat4
7602*7c478bd9Sstevel@tonic-gate rfs4_do_lock(rfs4_lo_state_t *lp, nfs_lock_type4 locktype,
7603*7c478bd9Sstevel@tonic-gate 	    seqid4 seqid, offset4 offset,
7604*7c478bd9Sstevel@tonic-gate 	    length4 length, cred_t *cred, nfs_resop4 *resop)
7605*7c478bd9Sstevel@tonic-gate {
7606*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
7607*7c478bd9Sstevel@tonic-gate 	rfs4_lockowner_t *lo = lp->locker;
7608*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *sp = lp->state;
7609*7c478bd9Sstevel@tonic-gate 	struct flock64 flock;
7610*7c478bd9Sstevel@tonic-gate 	int16_t ltype;
7611*7c478bd9Sstevel@tonic-gate 	int flag;
7612*7c478bd9Sstevel@tonic-gate 	int error;
7613*7c478bd9Sstevel@tonic-gate 	sysid_t sysid;
7614*7c478bd9Sstevel@tonic-gate 	LOCK4res *lres;
7615*7c478bd9Sstevel@tonic-gate 
7616*7c478bd9Sstevel@tonic-gate 	if (rfs4_lease_expired(lo->client)) {
7617*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_EXPIRED);
7618*7c478bd9Sstevel@tonic-gate 	}
7619*7c478bd9Sstevel@tonic-gate 
7620*7c478bd9Sstevel@tonic-gate 	if ((status = rfs4_client_sysid(lo->client, &sysid)) != NFS4_OK)
7621*7c478bd9Sstevel@tonic-gate 		return (status);
7622*7c478bd9Sstevel@tonic-gate 
7623*7c478bd9Sstevel@tonic-gate 	/* Check for zero length. To lock to end of file use all ones for V4 */
7624*7c478bd9Sstevel@tonic-gate 	if (length == 0)
7625*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
7626*7c478bd9Sstevel@tonic-gate 	else if (length == (length4)(~0))
7627*7c478bd9Sstevel@tonic-gate 		length = 0;		/* Posix to end of file  */
7628*7c478bd9Sstevel@tonic-gate 
7629*7c478bd9Sstevel@tonic-gate retry:
7630*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_lock(sp->dbe);
7631*7c478bd9Sstevel@tonic-gate 
7632*7c478bd9Sstevel@tonic-gate 
7633*7c478bd9Sstevel@tonic-gate 	if (resop->resop != OP_LOCKU) {
7634*7c478bd9Sstevel@tonic-gate 		switch (locktype) {
7635*7c478bd9Sstevel@tonic-gate 		case READ_LT:
7636*7c478bd9Sstevel@tonic-gate 		case READW_LT:
7637*7c478bd9Sstevel@tonic-gate 			if ((sp->share_access
7638*7c478bd9Sstevel@tonic-gate 			    & OPEN4_SHARE_ACCESS_READ) == 0) {
7639*7c478bd9Sstevel@tonic-gate 				rfs4_dbe_unlock(sp->dbe);
7640*7c478bd9Sstevel@tonic-gate 
7641*7c478bd9Sstevel@tonic-gate 				return (NFS4ERR_OPENMODE);
7642*7c478bd9Sstevel@tonic-gate 			}
7643*7c478bd9Sstevel@tonic-gate 			ltype = F_RDLCK;
7644*7c478bd9Sstevel@tonic-gate 			break;
7645*7c478bd9Sstevel@tonic-gate 		case WRITE_LT:
7646*7c478bd9Sstevel@tonic-gate 		case WRITEW_LT:
7647*7c478bd9Sstevel@tonic-gate 			if ((sp->share_access
7648*7c478bd9Sstevel@tonic-gate 			    & OPEN4_SHARE_ACCESS_WRITE) == 0) {
7649*7c478bd9Sstevel@tonic-gate 				rfs4_dbe_unlock(sp->dbe);
7650*7c478bd9Sstevel@tonic-gate 
7651*7c478bd9Sstevel@tonic-gate 				return (NFS4ERR_OPENMODE);
7652*7c478bd9Sstevel@tonic-gate 			}
7653*7c478bd9Sstevel@tonic-gate 			ltype = F_WRLCK;
7654*7c478bd9Sstevel@tonic-gate 			break;
7655*7c478bd9Sstevel@tonic-gate 		}
7656*7c478bd9Sstevel@tonic-gate 	} else
7657*7c478bd9Sstevel@tonic-gate 		ltype = F_UNLCK;
7658*7c478bd9Sstevel@tonic-gate 
7659*7c478bd9Sstevel@tonic-gate 	flock.l_type = ltype;
7660*7c478bd9Sstevel@tonic-gate 	flock.l_whence = 0;		/* SEEK_SET */
7661*7c478bd9Sstevel@tonic-gate 	flock.l_start = offset;
7662*7c478bd9Sstevel@tonic-gate 	flock.l_len = length;
7663*7c478bd9Sstevel@tonic-gate 	flock.l_sysid = sysid;
7664*7c478bd9Sstevel@tonic-gate 	flock.l_pid = lp->locker->pid;
7665*7c478bd9Sstevel@tonic-gate 
7666*7c478bd9Sstevel@tonic-gate 	/* Note that length4 is uint64_t but l_len and l_start are off64_t */
7667*7c478bd9Sstevel@tonic-gate 	if (flock.l_len < 0 || flock.l_start < 0) {
7668*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(sp->dbe);
7669*7c478bd9Sstevel@tonic-gate 		return (NFS4ERR_INVAL);
7670*7c478bd9Sstevel@tonic-gate 	}
7671*7c478bd9Sstevel@tonic-gate 
7672*7c478bd9Sstevel@tonic-gate 	/*
7673*7c478bd9Sstevel@tonic-gate 	 * N.B. FREAD has the same value as OPEN4_SHARE_ACCESS_READ and
7674*7c478bd9Sstevel@tonic-gate 	 * FWRITE has the same value as OPEN4_SHARE_ACCESS_WRITE.
7675*7c478bd9Sstevel@tonic-gate 	 */
7676*7c478bd9Sstevel@tonic-gate 	flag = (int)sp->share_access | F_REMOTELOCK;
7677*7c478bd9Sstevel@tonic-gate 
7678*7c478bd9Sstevel@tonic-gate 	error = setlock(sp->finfo->vp, &flock, flag, cred);
7679*7c478bd9Sstevel@tonic-gate 	if (error == 0) {
7680*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(lp->dbe);
7681*7c478bd9Sstevel@tonic-gate 		next_stateid(&lp->lockid);
7682*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(lp->dbe);
7683*7c478bd9Sstevel@tonic-gate 	}
7684*7c478bd9Sstevel@tonic-gate 
7685*7c478bd9Sstevel@tonic-gate 	rfs4_dbe_unlock(sp->dbe);
7686*7c478bd9Sstevel@tonic-gate 
7687*7c478bd9Sstevel@tonic-gate 	/*
7688*7c478bd9Sstevel@tonic-gate 	 * N.B. We map error values to nfsv4 errors. This is differrent
7689*7c478bd9Sstevel@tonic-gate 	 * than puterrno4 routine.
7690*7c478bd9Sstevel@tonic-gate 	 */
7691*7c478bd9Sstevel@tonic-gate 	switch (error) {
7692*7c478bd9Sstevel@tonic-gate 	case 0:
7693*7c478bd9Sstevel@tonic-gate 		status = NFS4_OK;
7694*7c478bd9Sstevel@tonic-gate 		break;
7695*7c478bd9Sstevel@tonic-gate 	case EAGAIN:
7696*7c478bd9Sstevel@tonic-gate 	case EACCES:		/* Old value */
7697*7c478bd9Sstevel@tonic-gate 		/* Can only get here if op is OP_LOCK */
7698*7c478bd9Sstevel@tonic-gate 		ASSERT(resop->resop == OP_LOCK);
7699*7c478bd9Sstevel@tonic-gate 		lres = &resop->nfs_resop4_u.oplock;
7700*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_DENIED;
7701*7c478bd9Sstevel@tonic-gate 		if (lock_denied(&lres->LOCK4res_u.denied, &flock)
7702*7c478bd9Sstevel@tonic-gate 			== NFS4ERR_EXPIRED)
7703*7c478bd9Sstevel@tonic-gate 			goto retry;
7704*7c478bd9Sstevel@tonic-gate 		break;
7705*7c478bd9Sstevel@tonic-gate 	case ENOLCK:
7706*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_DELAY;
7707*7c478bd9Sstevel@tonic-gate 		break;
7708*7c478bd9Sstevel@tonic-gate 	case EOVERFLOW:
7709*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_INVAL;
7710*7c478bd9Sstevel@tonic-gate 		break;
7711*7c478bd9Sstevel@tonic-gate 	case EINVAL:
7712*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_NOTSUPP;
7713*7c478bd9Sstevel@tonic-gate 		break;
7714*7c478bd9Sstevel@tonic-gate 	default:
7715*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "rfs4_do_lock: unexpected errno (%d)",
7716*7c478bd9Sstevel@tonic-gate 			error);
7717*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_SERVERFAULT;
7718*7c478bd9Sstevel@tonic-gate 		break;
7719*7c478bd9Sstevel@tonic-gate 	}
7720*7c478bd9Sstevel@tonic-gate 
7721*7c478bd9Sstevel@tonic-gate 	return (status);
7722*7c478bd9Sstevel@tonic-gate }
7723*7c478bd9Sstevel@tonic-gate 
7724*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7725*7c478bd9Sstevel@tonic-gate void
7726*7c478bd9Sstevel@tonic-gate rfs4_op_lock(nfs_argop4 *argop, nfs_resop4 *resop,
7727*7c478bd9Sstevel@tonic-gate 	    struct svc_req *req, struct compound_state *cs)
7728*7c478bd9Sstevel@tonic-gate {
7729*7c478bd9Sstevel@tonic-gate 	/* XXX Currently not using req arg */
7730*7c478bd9Sstevel@tonic-gate 	LOCK4args *args = &argop->nfs_argop4_u.oplock;
7731*7c478bd9Sstevel@tonic-gate 	LOCK4res *resp = &resop->nfs_resop4_u.oplock;
7732*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
7733*7c478bd9Sstevel@tonic-gate 	stateid4 *stateid;
7734*7c478bd9Sstevel@tonic-gate 	rfs4_lockowner_t *lo;
7735*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp;
7736*7c478bd9Sstevel@tonic-gate 	rfs4_state_t *sp = NULL;
7737*7c478bd9Sstevel@tonic-gate 	rfs4_lo_state_t *lsp = NULL;
7738*7c478bd9Sstevel@tonic-gate 	bool_t ls_sw_held = FALSE;
7739*7c478bd9Sstevel@tonic-gate 	bool_t create = TRUE;
7740*7c478bd9Sstevel@tonic-gate 	bool_t lcreate = TRUE;
7741*7c478bd9Sstevel@tonic-gate 	bool_t dup_lock = FALSE;
7742*7c478bd9Sstevel@tonic-gate 	int rc;
7743*7c478bd9Sstevel@tonic-gate 
7744*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
7745*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
7746*7c478bd9Sstevel@tonic-gate 		return;
7747*7c478bd9Sstevel@tonic-gate 	}
7748*7c478bd9Sstevel@tonic-gate 
7749*7c478bd9Sstevel@tonic-gate 	if (args->locker.new_lock_owner) {
7750*7c478bd9Sstevel@tonic-gate 		/* Create a new lockowner for this instance */
7751*7c478bd9Sstevel@tonic-gate 		open_to_lock_owner4 *olo = &args->locker.locker4_u.open_owner;
7752*7c478bd9Sstevel@tonic-gate 
7753*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug, (CE_NOTE, "Creating new lock owner"));
7754*7c478bd9Sstevel@tonic-gate 
7755*7c478bd9Sstevel@tonic-gate 		stateid = &olo->open_stateid;
7756*7c478bd9Sstevel@tonic-gate 		status = rfs4_get_state(stateid, &sp, RFS4_DBS_VALID);
7757*7c478bd9Sstevel@tonic-gate 		if (status != NFS4_OK) {
7758*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug,
7759*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "Get state failed in lock %d",
7760*7c478bd9Sstevel@tonic-gate 				status));
7761*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = status;
7762*7c478bd9Sstevel@tonic-gate 			return;
7763*7c478bd9Sstevel@tonic-gate 		}
7764*7c478bd9Sstevel@tonic-gate 
7765*7c478bd9Sstevel@tonic-gate 		/* Ensure specified filehandle matches */
7766*7c478bd9Sstevel@tonic-gate 		if (cs->vp != sp->finfo->vp) {
7767*7c478bd9Sstevel@tonic-gate 			rfs4_state_rele(sp);
7768*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7769*7c478bd9Sstevel@tonic-gate 			return;
7770*7c478bd9Sstevel@tonic-gate 		}
7771*7c478bd9Sstevel@tonic-gate 
7772*7c478bd9Sstevel@tonic-gate 		/* hold off other access to open_owner while we tinker */
7773*7c478bd9Sstevel@tonic-gate 		rfs4_sw_enter(&sp->owner->oo_sw);
7774*7c478bd9Sstevel@tonic-gate 
7775*7c478bd9Sstevel@tonic-gate 		switch (rc = rfs4_check_stateid_seqid(sp, stateid)) {
7776*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_OLD:
7777*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
7778*7c478bd9Sstevel@tonic-gate 			goto end;
7779*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_BAD:
7780*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7781*7c478bd9Sstevel@tonic-gate 			goto end;
7782*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_EXPIRED:
7783*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_EXPIRED;
7784*7c478bd9Sstevel@tonic-gate 			goto end;
7785*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_UNCONFIRMED:
7786*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7787*7c478bd9Sstevel@tonic-gate 			goto end;
7788*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_CLOSED:
7789*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
7790*7c478bd9Sstevel@tonic-gate 			goto end;
7791*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_OKAY:
7792*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_REPLAY:
7793*7c478bd9Sstevel@tonic-gate 			switch (rfs4_check_olo_seqid(olo->open_seqid,
7794*7c478bd9Sstevel@tonic-gate 				sp->owner, resop)) {
7795*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_OKAY:
7796*7c478bd9Sstevel@tonic-gate 				if (rc == NFS4_CHECK_STATEID_OKAY)
7797*7c478bd9Sstevel@tonic-gate 					break;
7798*7c478bd9Sstevel@tonic-gate 				/*
7799*7c478bd9Sstevel@tonic-gate 				 * This is replayed stateid; if seqid
7800*7c478bd9Sstevel@tonic-gate 				 * matches next expected, then client
7801*7c478bd9Sstevel@tonic-gate 				 * is using wrong seqid.
7802*7c478bd9Sstevel@tonic-gate 				 */
7803*7c478bd9Sstevel@tonic-gate 				/* FALLTHROUGH */
7804*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_BAD:
7805*7c478bd9Sstevel@tonic-gate 				*cs->statusp = resp->status =
7806*7c478bd9Sstevel@tonic-gate 					NFS4ERR_BAD_SEQID;
7807*7c478bd9Sstevel@tonic-gate 				goto end;
7808*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_REPLAY:
7809*7c478bd9Sstevel@tonic-gate 				/* This is a duplicate LOCK request */
7810*7c478bd9Sstevel@tonic-gate 				dup_lock = TRUE;
7811*7c478bd9Sstevel@tonic-gate 
7812*7c478bd9Sstevel@tonic-gate 				/*
7813*7c478bd9Sstevel@tonic-gate 				 * For a duplicate we do not want to
7814*7c478bd9Sstevel@tonic-gate 				 * create a new lockowner as it should
7815*7c478bd9Sstevel@tonic-gate 				 * already exist.
7816*7c478bd9Sstevel@tonic-gate 				 * Turn off the lockowner create flag.
7817*7c478bd9Sstevel@tonic-gate 				 */
7818*7c478bd9Sstevel@tonic-gate 				lcreate = FALSE;
7819*7c478bd9Sstevel@tonic-gate 			}
7820*7c478bd9Sstevel@tonic-gate 			break;
7821*7c478bd9Sstevel@tonic-gate 		}
7822*7c478bd9Sstevel@tonic-gate 
7823*7c478bd9Sstevel@tonic-gate 		lo = rfs4_findlockowner(&olo->lock_owner, &lcreate);
7824*7c478bd9Sstevel@tonic-gate 		if (lo == NULL) {
7825*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug,
7826*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "rfs4_op_lock: no lock owner"));
7827*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_RESOURCE;
7828*7c478bd9Sstevel@tonic-gate 			goto end;
7829*7c478bd9Sstevel@tonic-gate 		}
7830*7c478bd9Sstevel@tonic-gate 
7831*7c478bd9Sstevel@tonic-gate 		lsp = rfs4_findlo_state_by_owner(lo, sp, &create);
7832*7c478bd9Sstevel@tonic-gate 		if (lsp == NULL) {
7833*7c478bd9Sstevel@tonic-gate 			rfs4_update_lease(sp->owner->client);
7834*7c478bd9Sstevel@tonic-gate 			/*
7835*7c478bd9Sstevel@tonic-gate 			 * Only update theh open_seqid if this is not
7836*7c478bd9Sstevel@tonic-gate 			 * a duplicate request
7837*7c478bd9Sstevel@tonic-gate 			 */
7838*7c478bd9Sstevel@tonic-gate 			if (dup_lock == FALSE) {
7839*7c478bd9Sstevel@tonic-gate 				rfs4_update_open_sequence(sp->owner);
7840*7c478bd9Sstevel@tonic-gate 			}
7841*7c478bd9Sstevel@tonic-gate 
7842*7c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(rfs4_debug,
7843*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "rfs4_op_lock: no state"));
7844*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_SERVERFAULT;
7845*7c478bd9Sstevel@tonic-gate 			rfs4_update_open_resp(sp->owner, resop, NULL);
7846*7c478bd9Sstevel@tonic-gate 			rfs4_lockowner_rele(lo);
7847*7c478bd9Sstevel@tonic-gate 			goto end;
7848*7c478bd9Sstevel@tonic-gate 		}
7849*7c478bd9Sstevel@tonic-gate 
7850*7c478bd9Sstevel@tonic-gate 		/*
7851*7c478bd9Sstevel@tonic-gate 		 * This is the new_lock_owner branch and the client is
7852*7c478bd9Sstevel@tonic-gate 		 * supposed to be associating a new lock_owner with
7853*7c478bd9Sstevel@tonic-gate 		 * the open file at this point.  If we find that a
7854*7c478bd9Sstevel@tonic-gate 		 * lock_owner/state association already exists and a
7855*7c478bd9Sstevel@tonic-gate 		 * successful LOCK request was returned to the client,
7856*7c478bd9Sstevel@tonic-gate 		 * an error is returned to the client since this is
7857*7c478bd9Sstevel@tonic-gate 		 * not appropriate.  The client should be using the
7858*7c478bd9Sstevel@tonic-gate 		 * existing lock_owner branch.
7859*7c478bd9Sstevel@tonic-gate 		 */
7860*7c478bd9Sstevel@tonic-gate 		if (dup_lock == FALSE && create == FALSE) {
7861*7c478bd9Sstevel@tonic-gate 			if (lsp->lock_completed == TRUE) {
7862*7c478bd9Sstevel@tonic-gate 				*cs->statusp =
7863*7c478bd9Sstevel@tonic-gate 					resp->status = NFS4ERR_BAD_SEQID;
7864*7c478bd9Sstevel@tonic-gate 				rfs4_lockowner_rele(lo);
7865*7c478bd9Sstevel@tonic-gate 				goto end;
7866*7c478bd9Sstevel@tonic-gate 			}
7867*7c478bd9Sstevel@tonic-gate 		}
7868*7c478bd9Sstevel@tonic-gate 
7869*7c478bd9Sstevel@tonic-gate 		rfs4_update_lease(sp->owner->client);
7870*7c478bd9Sstevel@tonic-gate 
7871*7c478bd9Sstevel@tonic-gate 		/*
7872*7c478bd9Sstevel@tonic-gate 		 * Only update theh open_seqid if this is not
7873*7c478bd9Sstevel@tonic-gate 		 * a duplicate request
7874*7c478bd9Sstevel@tonic-gate 		 */
7875*7c478bd9Sstevel@tonic-gate 		if (dup_lock == FALSE) {
7876*7c478bd9Sstevel@tonic-gate 			rfs4_update_open_sequence(sp->owner);
7877*7c478bd9Sstevel@tonic-gate 		}
7878*7c478bd9Sstevel@tonic-gate 
7879*7c478bd9Sstevel@tonic-gate 		/*
7880*7c478bd9Sstevel@tonic-gate 		 * If this is a duplicate lock request, just copy the
7881*7c478bd9Sstevel@tonic-gate 		 * previously saved reply and return.
7882*7c478bd9Sstevel@tonic-gate 		 */
7883*7c478bd9Sstevel@tonic-gate 		if (dup_lock == TRUE) {
7884*7c478bd9Sstevel@tonic-gate 			/* verify that lock_seqid's match */
7885*7c478bd9Sstevel@tonic-gate 			if (lsp->seqid != olo->lock_seqid) {
7886*7c478bd9Sstevel@tonic-gate 				NFS4_DEBUG(rfs4_debug,
7887*7c478bd9Sstevel@tonic-gate 				(CE_NOTE, "rfs4_op_lock: Dup-Lock seqid bad"
7888*7c478bd9Sstevel@tonic-gate 				"lsp->seqid=%d old->seqid=%d",
7889*7c478bd9Sstevel@tonic-gate 				lsp->seqid, olo->lock_seqid));
7890*7c478bd9Sstevel@tonic-gate 				*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
7891*7c478bd9Sstevel@tonic-gate 			} else {
7892*7c478bd9Sstevel@tonic-gate 				rfs4_copy_reply(resop, lsp->reply);
7893*7c478bd9Sstevel@tonic-gate 				/*
7894*7c478bd9Sstevel@tonic-gate 				 * Make sure to copy the just
7895*7c478bd9Sstevel@tonic-gate 				 * retrieved reply status into the
7896*7c478bd9Sstevel@tonic-gate 				 * overall compound status
7897*7c478bd9Sstevel@tonic-gate 				 */
7898*7c478bd9Sstevel@tonic-gate 				*cs->statusp = resp->status;
7899*7c478bd9Sstevel@tonic-gate 			}
7900*7c478bd9Sstevel@tonic-gate 			rfs4_lockowner_rele(lo);
7901*7c478bd9Sstevel@tonic-gate 			goto end;
7902*7c478bd9Sstevel@tonic-gate 		}
7903*7c478bd9Sstevel@tonic-gate 
7904*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_lock(lsp->dbe);
7905*7c478bd9Sstevel@tonic-gate 
7906*7c478bd9Sstevel@tonic-gate 		/* Make sure to update the lock sequence id */
7907*7c478bd9Sstevel@tonic-gate 		lsp->seqid = olo->lock_seqid;
7908*7c478bd9Sstevel@tonic-gate 
7909*7c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(rfs4_debug,
7910*7c478bd9Sstevel@tonic-gate 			(CE_NOTE, "Lock seqid established as %d", lsp->seqid));
7911*7c478bd9Sstevel@tonic-gate 
7912*7c478bd9Sstevel@tonic-gate 		/*
7913*7c478bd9Sstevel@tonic-gate 		 * This is used to signify the newly created lockowner
7914*7c478bd9Sstevel@tonic-gate 		 * stateid and its sequence number.  The checks for
7915*7c478bd9Sstevel@tonic-gate 		 * sequence number and increment don't occur on the
7916*7c478bd9Sstevel@tonic-gate 		 * very first lock request for a lockowner.
7917*7c478bd9Sstevel@tonic-gate 		 */
7918*7c478bd9Sstevel@tonic-gate 		lsp->skip_seqid_check = TRUE;
7919*7c478bd9Sstevel@tonic-gate 
7920*7c478bd9Sstevel@tonic-gate 		/* hold off other access to lsp while we tinker */
7921*7c478bd9Sstevel@tonic-gate 		rfs4_sw_enter(&lsp->ls_sw);
7922*7c478bd9Sstevel@tonic-gate 		ls_sw_held = TRUE;
7923*7c478bd9Sstevel@tonic-gate 
7924*7c478bd9Sstevel@tonic-gate 		rfs4_dbe_unlock(lsp->dbe);
7925*7c478bd9Sstevel@tonic-gate 
7926*7c478bd9Sstevel@tonic-gate 		rfs4_lockowner_rele(lo);
7927*7c478bd9Sstevel@tonic-gate 	} else {
7928*7c478bd9Sstevel@tonic-gate 		stateid = &args->locker.locker4_u.lock_owner.lock_stateid;
7929*7c478bd9Sstevel@tonic-gate 		/* get lsp and hold the lock on the underlying file struct */
7930*7c478bd9Sstevel@tonic-gate 		if ((status = rfs4_get_lo_state(stateid, &lsp, TRUE))
7931*7c478bd9Sstevel@tonic-gate 		    != NFS4_OK) {
7932*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = status;
7933*7c478bd9Sstevel@tonic-gate 			return;
7934*7c478bd9Sstevel@tonic-gate 		}
7935*7c478bd9Sstevel@tonic-gate 		create = FALSE;	/* We didn't create lsp */
7936*7c478bd9Sstevel@tonic-gate 
7937*7c478bd9Sstevel@tonic-gate 		/* Ensure specified filehandle matches */
7938*7c478bd9Sstevel@tonic-gate 		if (cs->vp != lsp->state->finfo->vp) {
7939*7c478bd9Sstevel@tonic-gate 			rfs4_lo_state_rele(lsp, TRUE);
7940*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
7941*7c478bd9Sstevel@tonic-gate 			return;
7942*7c478bd9Sstevel@tonic-gate 		}
7943*7c478bd9Sstevel@tonic-gate 
7944*7c478bd9Sstevel@tonic-gate 		/* hold off other access to lsp while we tinker */
7945*7c478bd9Sstevel@tonic-gate 		rfs4_sw_enter(&lsp->ls_sw);
7946*7c478bd9Sstevel@tonic-gate 		ls_sw_held = TRUE;
7947*7c478bd9Sstevel@tonic-gate 
7948*7c478bd9Sstevel@tonic-gate 		switch (rfs4_check_lo_stateid_seqid(lsp, stateid)) {
7949*7c478bd9Sstevel@tonic-gate 		/*
7950*7c478bd9Sstevel@tonic-gate 		 * The stateid looks like it was okay (expected to be
7951*7c478bd9Sstevel@tonic-gate 		 * the next one)
7952*7c478bd9Sstevel@tonic-gate 		 */
7953*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_OKAY:
7954*7c478bd9Sstevel@tonic-gate 			/*
7955*7c478bd9Sstevel@tonic-gate 			 * The sequence id is now checked.  Determine
7956*7c478bd9Sstevel@tonic-gate 			 * if this is a replay or if it is in the
7957*7c478bd9Sstevel@tonic-gate 			 * expected (next) sequence.  In the case of a
7958*7c478bd9Sstevel@tonic-gate 			 * replay, there are two replay conditions
7959*7c478bd9Sstevel@tonic-gate 			 * that may occur.  The first is the normal
7960*7c478bd9Sstevel@tonic-gate 			 * condition where a LOCK is done with a
7961*7c478bd9Sstevel@tonic-gate 			 * NFS4_OK response and the stateid is
7962*7c478bd9Sstevel@tonic-gate 			 * updated.  That case is handled below when
7963*7c478bd9Sstevel@tonic-gate 			 * the stateid is identified as a REPLAY.  The
7964*7c478bd9Sstevel@tonic-gate 			 * second is the case where an error is
7965*7c478bd9Sstevel@tonic-gate 			 * returned, like NFS4ERR_DENIED, and the
7966*7c478bd9Sstevel@tonic-gate 			 * sequence number is updated but the stateid
7967*7c478bd9Sstevel@tonic-gate 			 * is not updated.  This second case is dealt
7968*7c478bd9Sstevel@tonic-gate 			 * with here.  So it may seem odd that the
7969*7c478bd9Sstevel@tonic-gate 			 * stateid is okay but the sequence id is a
7970*7c478bd9Sstevel@tonic-gate 			 * replay but it is okay.
7971*7c478bd9Sstevel@tonic-gate 			 */
7972*7c478bd9Sstevel@tonic-gate 			switch (rfs4_check_lock_seqid(
7973*7c478bd9Sstevel@tonic-gate 				args->locker.locker4_u.lock_owner.lock_seqid,
7974*7c478bd9Sstevel@tonic-gate 				lsp, resop)) {
7975*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_REPLAY:
7976*7c478bd9Sstevel@tonic-gate 				if (resp->status != NFS4_OK) {
7977*7c478bd9Sstevel@tonic-gate 					/*
7978*7c478bd9Sstevel@tonic-gate 					 * Here is our replay and need
7979*7c478bd9Sstevel@tonic-gate 					 * to verify that the last
7980*7c478bd9Sstevel@tonic-gate 					 * response was an error.
7981*7c478bd9Sstevel@tonic-gate 					 */
7982*7c478bd9Sstevel@tonic-gate 					*cs->statusp = resp->status;
7983*7c478bd9Sstevel@tonic-gate 					goto end;
7984*7c478bd9Sstevel@tonic-gate 				}
7985*7c478bd9Sstevel@tonic-gate 				/*
7986*7c478bd9Sstevel@tonic-gate 				 * This is done since the sequence id
7987*7c478bd9Sstevel@tonic-gate 				 * looked like a replay but it didn't
7988*7c478bd9Sstevel@tonic-gate 				 * pass our check so a BAD_SEQID is
7989*7c478bd9Sstevel@tonic-gate 				 * returned as a result.
7990*7c478bd9Sstevel@tonic-gate 				 */
7991*7c478bd9Sstevel@tonic-gate 				/*FALLTHROUGH*/
7992*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_BAD:
7993*7c478bd9Sstevel@tonic-gate 				*cs->statusp = resp->status =
7994*7c478bd9Sstevel@tonic-gate 					NFS4ERR_BAD_SEQID;
7995*7c478bd9Sstevel@tonic-gate 				goto end;
7996*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_OKAY:
7997*7c478bd9Sstevel@tonic-gate 				/* Everything looks okay move ahead */
7998*7c478bd9Sstevel@tonic-gate 				break;
7999*7c478bd9Sstevel@tonic-gate 			}
8000*7c478bd9Sstevel@tonic-gate 			break;
8001*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_OLD:
8002*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
8003*7c478bd9Sstevel@tonic-gate 			goto end;
8004*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_BAD:
8005*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
8006*7c478bd9Sstevel@tonic-gate 			goto end;
8007*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_EXPIRED:
8008*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_EXPIRED;
8009*7c478bd9Sstevel@tonic-gate 			goto end;
8010*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_CLOSED:
8011*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
8012*7c478bd9Sstevel@tonic-gate 			goto end;
8013*7c478bd9Sstevel@tonic-gate 		case NFS4_CHECK_STATEID_REPLAY:
8014*7c478bd9Sstevel@tonic-gate 			switch (rfs4_check_lock_seqid(
8015*7c478bd9Sstevel@tonic-gate 				args->locker.locker4_u.lock_owner.lock_seqid,
8016*7c478bd9Sstevel@tonic-gate 				lsp, resop)) {
8017*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_OKAY:
8018*7c478bd9Sstevel@tonic-gate 				/*
8019*7c478bd9Sstevel@tonic-gate 				 * This is a replayed stateid; if
8020*7c478bd9Sstevel@tonic-gate 				 * seqid matches the next expected,
8021*7c478bd9Sstevel@tonic-gate 				 * then client is using wrong seqid.
8022*7c478bd9Sstevel@tonic-gate 				 */
8023*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_BAD:
8024*7c478bd9Sstevel@tonic-gate 				*cs->statusp = resp->status =
8025*7c478bd9Sstevel@tonic-gate 					NFS4ERR_BAD_SEQID;
8026*7c478bd9Sstevel@tonic-gate 				goto end;
8027*7c478bd9Sstevel@tonic-gate 			case NFS4_CHKSEQ_REPLAY:
8028*7c478bd9Sstevel@tonic-gate 				rfs4_update_lease(lsp->locker->client);
8029*7c478bd9Sstevel@tonic-gate 				*cs->statusp = status = resp->status;
8030*7c478bd9Sstevel@tonic-gate 				goto end;
8031*7c478bd9Sstevel@tonic-gate 			}
8032*7c478bd9Sstevel@tonic-gate 			break;
8033*7c478bd9Sstevel@tonic-gate 		default:
8034*7c478bd9Sstevel@tonic-gate 			ASSERT(FALSE);
8035*7c478bd9Sstevel@tonic-gate 			break;
8036*7c478bd9Sstevel@tonic-gate 		}
8037*7c478bd9Sstevel@tonic-gate 
8038*7c478bd9Sstevel@tonic-gate 		rfs4_update_lock_sequence(lsp);
8039*7c478bd9Sstevel@tonic-gate 		rfs4_update_lease(lsp->locker->client);
8040*7c478bd9Sstevel@tonic-gate 	}
8041*7c478bd9Sstevel@tonic-gate 
8042*7c478bd9Sstevel@tonic-gate 	/*
8043*7c478bd9Sstevel@tonic-gate 	 * NFS4 only allows locking on regular files, so
8044*7c478bd9Sstevel@tonic-gate 	 * verify type of object.
8045*7c478bd9Sstevel@tonic-gate 	 */
8046*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VREG) {
8047*7c478bd9Sstevel@tonic-gate 		if (cs->vp->v_type == VDIR)
8048*7c478bd9Sstevel@tonic-gate 			status = NFS4ERR_ISDIR;
8049*7c478bd9Sstevel@tonic-gate 		else
8050*7c478bd9Sstevel@tonic-gate 			status = NFS4ERR_INVAL;
8051*7c478bd9Sstevel@tonic-gate 		goto out;
8052*7c478bd9Sstevel@tonic-gate 	}
8053*7c478bd9Sstevel@tonic-gate 
8054*7c478bd9Sstevel@tonic-gate 	cp = lsp->state->owner->client;
8055*7c478bd9Sstevel@tonic-gate 
8056*7c478bd9Sstevel@tonic-gate 	if (rfs4_clnt_in_grace(cp) && !args->reclaim) {
8057*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_GRACE;
8058*7c478bd9Sstevel@tonic-gate 		goto out;
8059*7c478bd9Sstevel@tonic-gate 	}
8060*7c478bd9Sstevel@tonic-gate 
8061*7c478bd9Sstevel@tonic-gate 	if (rfs4_clnt_in_grace(cp) && args->reclaim && !cp->can_reclaim) {
8062*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_NO_GRACE;
8063*7c478bd9Sstevel@tonic-gate 		goto out;
8064*7c478bd9Sstevel@tonic-gate 	}
8065*7c478bd9Sstevel@tonic-gate 
8066*7c478bd9Sstevel@tonic-gate 	if (!rfs4_clnt_in_grace(cp) && args->reclaim) {
8067*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_NO_GRACE;
8068*7c478bd9Sstevel@tonic-gate 		goto out;
8069*7c478bd9Sstevel@tonic-gate 	}
8070*7c478bd9Sstevel@tonic-gate 
8071*7c478bd9Sstevel@tonic-gate 	if (lsp->state->finfo->dinfo->dtype == OPEN_DELEGATE_WRITE)
8072*7c478bd9Sstevel@tonic-gate 		cs->deleg = TRUE;
8073*7c478bd9Sstevel@tonic-gate 
8074*7c478bd9Sstevel@tonic-gate 	status = rfs4_do_lock(lsp, args->locktype,
8075*7c478bd9Sstevel@tonic-gate 				args->locker.locker4_u.lock_owner.lock_seqid,
8076*7c478bd9Sstevel@tonic-gate 				args->offset,
8077*7c478bd9Sstevel@tonic-gate 				args->length, cs->cr, resop);
8078*7c478bd9Sstevel@tonic-gate 
8079*7c478bd9Sstevel@tonic-gate out:
8080*7c478bd9Sstevel@tonic-gate 	lsp->skip_seqid_check = FALSE;
8081*7c478bd9Sstevel@tonic-gate 
8082*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = status;
8083*7c478bd9Sstevel@tonic-gate 
8084*7c478bd9Sstevel@tonic-gate 	if (status == NFS4_OK) {
8085*7c478bd9Sstevel@tonic-gate 		resp->LOCK4res_u.lock_stateid = lsp->lockid.stateid;
8086*7c478bd9Sstevel@tonic-gate 		lsp->lock_completed = TRUE;
8087*7c478bd9Sstevel@tonic-gate 	}
8088*7c478bd9Sstevel@tonic-gate 	/*
8089*7c478bd9Sstevel@tonic-gate 	 * Only update the "OPEN" response here if this was a new
8090*7c478bd9Sstevel@tonic-gate 	 * lock_owner
8091*7c478bd9Sstevel@tonic-gate 	 */
8092*7c478bd9Sstevel@tonic-gate 	if (sp)
8093*7c478bd9Sstevel@tonic-gate 		rfs4_update_open_resp(sp->owner, resop, NULL);
8094*7c478bd9Sstevel@tonic-gate 
8095*7c478bd9Sstevel@tonic-gate 	rfs4_update_lock_resp(lsp, resop);
8096*7c478bd9Sstevel@tonic-gate 
8097*7c478bd9Sstevel@tonic-gate end:
8098*7c478bd9Sstevel@tonic-gate 	if (lsp) {
8099*7c478bd9Sstevel@tonic-gate 		if (ls_sw_held)
8100*7c478bd9Sstevel@tonic-gate 			rfs4_sw_exit(&lsp->ls_sw);
8101*7c478bd9Sstevel@tonic-gate 		/*
8102*7c478bd9Sstevel@tonic-gate 		 * If an sp obtained, then the lsp does not represent
8103*7c478bd9Sstevel@tonic-gate 		 * a lock on the file struct.
8104*7c478bd9Sstevel@tonic-gate 		 */
8105*7c478bd9Sstevel@tonic-gate 		if (sp != NULL)
8106*7c478bd9Sstevel@tonic-gate 			rfs4_lo_state_rele(lsp, FALSE);
8107*7c478bd9Sstevel@tonic-gate 		else
8108*7c478bd9Sstevel@tonic-gate 			rfs4_lo_state_rele(lsp, TRUE);
8109*7c478bd9Sstevel@tonic-gate 	}
8110*7c478bd9Sstevel@tonic-gate 	if (sp) {
8111*7c478bd9Sstevel@tonic-gate 		rfs4_sw_exit(&sp->owner->oo_sw);
8112*7c478bd9Sstevel@tonic-gate 		rfs4_state_rele(sp);
8113*7c478bd9Sstevel@tonic-gate 	}
8114*7c478bd9Sstevel@tonic-gate }
8115*7c478bd9Sstevel@tonic-gate 
8116*7c478bd9Sstevel@tonic-gate /* free function for LOCK/LOCKT */
8117*7c478bd9Sstevel@tonic-gate static void
8118*7c478bd9Sstevel@tonic-gate lock_denied_free(nfs_resop4 *resop)
8119*7c478bd9Sstevel@tonic-gate {
8120*7c478bd9Sstevel@tonic-gate 	LOCK4denied *dp = NULL;
8121*7c478bd9Sstevel@tonic-gate 
8122*7c478bd9Sstevel@tonic-gate 	switch (resop->resop) {
8123*7c478bd9Sstevel@tonic-gate 	case OP_LOCK:
8124*7c478bd9Sstevel@tonic-gate 		if (resop->nfs_resop4_u.oplock.status == NFS4ERR_DENIED)
8125*7c478bd9Sstevel@tonic-gate 			dp = &resop->nfs_resop4_u.oplock.LOCK4res_u.denied;
8126*7c478bd9Sstevel@tonic-gate 		break;
8127*7c478bd9Sstevel@tonic-gate 	case OP_LOCKT:
8128*7c478bd9Sstevel@tonic-gate 		if (resop->nfs_resop4_u.oplockt.status == NFS4ERR_DENIED)
8129*7c478bd9Sstevel@tonic-gate 			dp = &resop->nfs_resop4_u.oplockt.denied;
8130*7c478bd9Sstevel@tonic-gate 		break;
8131*7c478bd9Sstevel@tonic-gate 	default:
8132*7c478bd9Sstevel@tonic-gate 		break;
8133*7c478bd9Sstevel@tonic-gate 	}
8134*7c478bd9Sstevel@tonic-gate 
8135*7c478bd9Sstevel@tonic-gate 	if (dp)
8136*7c478bd9Sstevel@tonic-gate 		kmem_free(dp->owner.owner_val, dp->owner.owner_len);
8137*7c478bd9Sstevel@tonic-gate }
8138*7c478bd9Sstevel@tonic-gate 
8139*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8140*7c478bd9Sstevel@tonic-gate void
8141*7c478bd9Sstevel@tonic-gate rfs4_op_locku(nfs_argop4 *argop, nfs_resop4 *resop,
8142*7c478bd9Sstevel@tonic-gate 	    struct svc_req *req, struct compound_state *cs)
8143*7c478bd9Sstevel@tonic-gate {
8144*7c478bd9Sstevel@tonic-gate 	/* XXX Currently not using req arg */
8145*7c478bd9Sstevel@tonic-gate 	LOCKU4args *args = &argop->nfs_argop4_u.oplocku;
8146*7c478bd9Sstevel@tonic-gate 	LOCKU4res *resp = &resop->nfs_resop4_u.oplocku;
8147*7c478bd9Sstevel@tonic-gate 	nfsstat4 status;
8148*7c478bd9Sstevel@tonic-gate 	stateid4 *stateid = &args->lock_stateid;
8149*7c478bd9Sstevel@tonic-gate 	rfs4_lo_state_t *lsp;
8150*7c478bd9Sstevel@tonic-gate 
8151*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
8152*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
8153*7c478bd9Sstevel@tonic-gate 		return;
8154*7c478bd9Sstevel@tonic-gate 	}
8155*7c478bd9Sstevel@tonic-gate 
8156*7c478bd9Sstevel@tonic-gate 	if ((status = rfs4_get_lo_state(stateid, &lsp, TRUE)) != NFS4_OK) {
8157*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = status;
8158*7c478bd9Sstevel@tonic-gate 		return;
8159*7c478bd9Sstevel@tonic-gate 	}
8160*7c478bd9Sstevel@tonic-gate 
8161*7c478bd9Sstevel@tonic-gate 	/* Ensure specified filehandle matches */
8162*7c478bd9Sstevel@tonic-gate 	if (cs->vp != lsp->state->finfo->vp) {
8163*7c478bd9Sstevel@tonic-gate 		rfs4_lo_state_rele(lsp, TRUE);
8164*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
8165*7c478bd9Sstevel@tonic-gate 		return;
8166*7c478bd9Sstevel@tonic-gate 	}
8167*7c478bd9Sstevel@tonic-gate 
8168*7c478bd9Sstevel@tonic-gate 	/* hold off other access to lsp while we tinker */
8169*7c478bd9Sstevel@tonic-gate 	rfs4_sw_enter(&lsp->ls_sw);
8170*7c478bd9Sstevel@tonic-gate 
8171*7c478bd9Sstevel@tonic-gate 	switch (rfs4_check_lo_stateid_seqid(lsp, stateid)) {
8172*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OKAY:
8173*7c478bd9Sstevel@tonic-gate 		if (rfs4_check_lock_seqid(args->seqid, lsp, resop)
8174*7c478bd9Sstevel@tonic-gate 		    != NFS4_CHKSEQ_OKAY) {
8175*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_BAD_SEQID;
8176*7c478bd9Sstevel@tonic-gate 			goto end;
8177*7c478bd9Sstevel@tonic-gate 		}
8178*7c478bd9Sstevel@tonic-gate 		break;
8179*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_OLD:
8180*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
8181*7c478bd9Sstevel@tonic-gate 		goto end;
8182*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_BAD:
8183*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_BAD_STATEID;
8184*7c478bd9Sstevel@tonic-gate 		goto end;
8185*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_EXPIRED:
8186*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_EXPIRED;
8187*7c478bd9Sstevel@tonic-gate 		goto end;
8188*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_CLOSED:
8189*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_OLD_STATEID;
8190*7c478bd9Sstevel@tonic-gate 		goto end;
8191*7c478bd9Sstevel@tonic-gate 	case NFS4_CHECK_STATEID_REPLAY:
8192*7c478bd9Sstevel@tonic-gate 		switch (rfs4_check_lock_seqid(args->seqid, lsp, resop)) {
8193*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_OKAY:
8194*7c478bd9Sstevel@tonic-gate 				/*
8195*7c478bd9Sstevel@tonic-gate 				 * This is a replayed stateid; if
8196*7c478bd9Sstevel@tonic-gate 				 * seqid matches the next expected,
8197*7c478bd9Sstevel@tonic-gate 				 * then client is using wrong seqid.
8198*7c478bd9Sstevel@tonic-gate 				 */
8199*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_BAD:
8200*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status =
8201*7c478bd9Sstevel@tonic-gate 				NFS4ERR_BAD_SEQID;
8202*7c478bd9Sstevel@tonic-gate 			goto end;
8203*7c478bd9Sstevel@tonic-gate 		case NFS4_CHKSEQ_REPLAY:
8204*7c478bd9Sstevel@tonic-gate 			rfs4_update_lease(lsp->locker->client);
8205*7c478bd9Sstevel@tonic-gate 			*cs->statusp = status = resp->status;
8206*7c478bd9Sstevel@tonic-gate 			goto end;
8207*7c478bd9Sstevel@tonic-gate 		}
8208*7c478bd9Sstevel@tonic-gate 		break;
8209*7c478bd9Sstevel@tonic-gate 	default:
8210*7c478bd9Sstevel@tonic-gate 		ASSERT(FALSE);
8211*7c478bd9Sstevel@tonic-gate 		break;
8212*7c478bd9Sstevel@tonic-gate 	}
8213*7c478bd9Sstevel@tonic-gate 
8214*7c478bd9Sstevel@tonic-gate 	rfs4_update_lock_sequence(lsp);
8215*7c478bd9Sstevel@tonic-gate 	rfs4_update_lease(lsp->locker->client);
8216*7c478bd9Sstevel@tonic-gate 
8217*7c478bd9Sstevel@tonic-gate 	/*
8218*7c478bd9Sstevel@tonic-gate 	 * NFS4 only allows locking on regular files, so
8219*7c478bd9Sstevel@tonic-gate 	 * verify type of object.
8220*7c478bd9Sstevel@tonic-gate 	 */
8221*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VREG) {
8222*7c478bd9Sstevel@tonic-gate 		if (cs->vp->v_type == VDIR)
8223*7c478bd9Sstevel@tonic-gate 			status = NFS4ERR_ISDIR;
8224*7c478bd9Sstevel@tonic-gate 		else
8225*7c478bd9Sstevel@tonic-gate 			status = NFS4ERR_INVAL;
8226*7c478bd9Sstevel@tonic-gate 		goto out;
8227*7c478bd9Sstevel@tonic-gate 	}
8228*7c478bd9Sstevel@tonic-gate 
8229*7c478bd9Sstevel@tonic-gate 	if (rfs4_clnt_in_grace(lsp->state->owner->client)) {
8230*7c478bd9Sstevel@tonic-gate 		status = NFS4ERR_GRACE;
8231*7c478bd9Sstevel@tonic-gate 		goto out;
8232*7c478bd9Sstevel@tonic-gate 	}
8233*7c478bd9Sstevel@tonic-gate 
8234*7c478bd9Sstevel@tonic-gate 	status = rfs4_do_lock(lsp, args->locktype,
8235*7c478bd9Sstevel@tonic-gate 			    args->seqid, args->offset,
8236*7c478bd9Sstevel@tonic-gate 			    args->length, cs->cr, resop);
8237*7c478bd9Sstevel@tonic-gate 
8238*7c478bd9Sstevel@tonic-gate out:
8239*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status = status;
8240*7c478bd9Sstevel@tonic-gate 
8241*7c478bd9Sstevel@tonic-gate 	if (status == NFS4_OK)
8242*7c478bd9Sstevel@tonic-gate 		resp->lock_stateid = lsp->lockid.stateid;
8243*7c478bd9Sstevel@tonic-gate 
8244*7c478bd9Sstevel@tonic-gate 	rfs4_update_lock_resp(lsp, resop);
8245*7c478bd9Sstevel@tonic-gate 
8246*7c478bd9Sstevel@tonic-gate end:
8247*7c478bd9Sstevel@tonic-gate 	rfs4_sw_exit(&lsp->ls_sw);
8248*7c478bd9Sstevel@tonic-gate 	rfs4_lo_state_rele(lsp, TRUE);
8249*7c478bd9Sstevel@tonic-gate }
8250*7c478bd9Sstevel@tonic-gate 
8251*7c478bd9Sstevel@tonic-gate /*
8252*7c478bd9Sstevel@tonic-gate  * LOCKT is a best effort routine, the client can not be guaranteed that
8253*7c478bd9Sstevel@tonic-gate  * the status return is still in effect by the time the reply is received.
8254*7c478bd9Sstevel@tonic-gate  * They are numerous race conditions in this routine, but we are not required
8255*7c478bd9Sstevel@tonic-gate  * and can not be accurate.
8256*7c478bd9Sstevel@tonic-gate  */
8257*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8258*7c478bd9Sstevel@tonic-gate void
8259*7c478bd9Sstevel@tonic-gate rfs4_op_lockt(nfs_argop4 *argop, nfs_resop4 *resop,
8260*7c478bd9Sstevel@tonic-gate 	    struct svc_req *req, struct compound_state *cs)
8261*7c478bd9Sstevel@tonic-gate {
8262*7c478bd9Sstevel@tonic-gate 	LOCKT4args *args = &argop->nfs_argop4_u.oplockt;
8263*7c478bd9Sstevel@tonic-gate 	LOCKT4res *resp = &resop->nfs_resop4_u.oplockt;
8264*7c478bd9Sstevel@tonic-gate 	rfs4_lockowner_t *lo;
8265*7c478bd9Sstevel@tonic-gate 	rfs4_client_t *cp;
8266*7c478bd9Sstevel@tonic-gate 	bool_t create = FALSE;
8267*7c478bd9Sstevel@tonic-gate 	struct flock64 flk;
8268*7c478bd9Sstevel@tonic-gate 	int error;
8269*7c478bd9Sstevel@tonic-gate 	int flag = FREAD | FWRITE;
8270*7c478bd9Sstevel@tonic-gate 	int ltype;
8271*7c478bd9Sstevel@tonic-gate 	length4 posix_length;
8272*7c478bd9Sstevel@tonic-gate 	sysid_t sysid;
8273*7c478bd9Sstevel@tonic-gate 	pid_t pid;
8274*7c478bd9Sstevel@tonic-gate 
8275*7c478bd9Sstevel@tonic-gate 	if (cs->vp == NULL) {
8276*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE;
8277*7c478bd9Sstevel@tonic-gate 		return;
8278*7c478bd9Sstevel@tonic-gate 	}
8279*7c478bd9Sstevel@tonic-gate 
8280*7c478bd9Sstevel@tonic-gate 	/*
8281*7c478bd9Sstevel@tonic-gate 	 * NFS4 only allows locking on regular files, so
8282*7c478bd9Sstevel@tonic-gate 	 * verify type of object.
8283*7c478bd9Sstevel@tonic-gate 	 */
8284*7c478bd9Sstevel@tonic-gate 	if (cs->vp->v_type != VREG) {
8285*7c478bd9Sstevel@tonic-gate 		if (cs->vp->v_type == VDIR)
8286*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status = NFS4ERR_ISDIR;
8287*7c478bd9Sstevel@tonic-gate 		else
8288*7c478bd9Sstevel@tonic-gate 			*cs->statusp = resp->status =  NFS4ERR_INVAL;
8289*7c478bd9Sstevel@tonic-gate 		return;
8290*7c478bd9Sstevel@tonic-gate 	}
8291*7c478bd9Sstevel@tonic-gate 
8292*7c478bd9Sstevel@tonic-gate 	/*
8293*7c478bd9Sstevel@tonic-gate 	 * Check out the clientid to ensure the server knows about it
8294*7c478bd9Sstevel@tonic-gate 	 * so that we correctly inform the client of a server reboot.
8295*7c478bd9Sstevel@tonic-gate 	 */
8296*7c478bd9Sstevel@tonic-gate 	if ((cp = rfs4_findclient_by_id(args->owner.clientid, FALSE))
8297*7c478bd9Sstevel@tonic-gate 	    == NULL) {
8298*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status =
8299*7c478bd9Sstevel@tonic-gate 			rfs4_check_clientid(&args->owner.clientid, 0);
8300*7c478bd9Sstevel@tonic-gate 		return;
8301*7c478bd9Sstevel@tonic-gate 	}
8302*7c478bd9Sstevel@tonic-gate 	if (rfs4_lease_expired(cp)) {
8303*7c478bd9Sstevel@tonic-gate 		rfs4_client_close(cp);
8304*7c478bd9Sstevel@tonic-gate 		/*
8305*7c478bd9Sstevel@tonic-gate 		 * Protocol doesn't allow returning NFS4ERR_STALE as
8306*7c478bd9Sstevel@tonic-gate 		 * other operations do on this check so STALE_CLIENTID
8307*7c478bd9Sstevel@tonic-gate 		 * is returned instead
8308*7c478bd9Sstevel@tonic-gate 		 */
8309*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_STALE_CLIENTID;
8310*7c478bd9Sstevel@tonic-gate 		return;
8311*7c478bd9Sstevel@tonic-gate 	}
8312*7c478bd9Sstevel@tonic-gate 
8313*7c478bd9Sstevel@tonic-gate 	if (rfs4_clnt_in_grace(cp)) {
8314*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_GRACE;
8315*7c478bd9Sstevel@tonic-gate 		return;
8316*7c478bd9Sstevel@tonic-gate 	}
8317*7c478bd9Sstevel@tonic-gate 	rfs4_client_rele(cp);
8318*7c478bd9Sstevel@tonic-gate 
8319*7c478bd9Sstevel@tonic-gate 	resp->status = NFS4_OK;
8320*7c478bd9Sstevel@tonic-gate 
8321*7c478bd9Sstevel@tonic-gate 	switch (args->locktype) {
8322*7c478bd9Sstevel@tonic-gate 	case READ_LT:
8323*7c478bd9Sstevel@tonic-gate 	case READW_LT:
8324*7c478bd9Sstevel@tonic-gate 		ltype = F_RDLCK;
8325*7c478bd9Sstevel@tonic-gate 		break;
8326*7c478bd9Sstevel@tonic-gate 	case WRITE_LT:
8327*7c478bd9Sstevel@tonic-gate 	case WRITEW_LT:
8328*7c478bd9Sstevel@tonic-gate 		ltype = F_WRLCK;
8329*7c478bd9Sstevel@tonic-gate 		break;
8330*7c478bd9Sstevel@tonic-gate 	}
8331*7c478bd9Sstevel@tonic-gate 
8332*7c478bd9Sstevel@tonic-gate 	posix_length = args->length;
8333*7c478bd9Sstevel@tonic-gate 	/* Check for zero length. To lock to end of file use all ones for V4 */
8334*7c478bd9Sstevel@tonic-gate 	if (posix_length == 0) {
8335*7c478bd9Sstevel@tonic-gate 		*cs->statusp = resp->status = NFS4ERR_INVAL;
8336*7c478bd9Sstevel@tonic-gate 		return;
8337*7c478bd9Sstevel@tonic-gate 	} else if (posix_length == (length4)(~0)) {
8338*7c478bd9Sstevel@tonic-gate 		posix_length = 0;	/* Posix to end of file  */
8339*7c478bd9Sstevel@tonic-gate 	}
8340*7c478bd9Sstevel@tonic-gate 
8341*7c478bd9Sstevel@tonic-gate 	/* Find or create a lockowner */
8342*7c478bd9Sstevel@tonic-gate 	lo = rfs4_findlockowner(&args->owner, &create);
8343*7c478bd9Sstevel@tonic-gate 
8344*7c478bd9Sstevel@tonic-gate 	if (lo) {
8345*7c478bd9Sstevel@tonic-gate 		pid = lo->pid;
8346*7c478bd9Sstevel@tonic-gate 		if ((resp->status =
8347*7c478bd9Sstevel@tonic-gate 			rfs4_client_sysid(lo->client, &sysid)) != NFS4_OK)
8348*7c478bd9Sstevel@tonic-gate 		goto out;
8349*7c478bd9Sstevel@tonic-gate 	} else {
8350*7c478bd9Sstevel@tonic-gate 		pid = 0;
8351*7c478bd9Sstevel@tonic-gate 		sysid = lockt_sysid;
8352*7c478bd9Sstevel@tonic-gate 	}
8353*7c478bd9Sstevel@tonic-gate retry:
8354*7c478bd9Sstevel@tonic-gate 	flk.l_type = ltype;
8355*7c478bd9Sstevel@tonic-gate 	flk.l_whence = 0;		/* SEEK_SET */
8356*7c478bd9Sstevel@tonic-gate 	flk.l_start = args->offset;
8357*7c478bd9Sstevel@tonic-gate 	flk.l_len = posix_length;
8358*7c478bd9Sstevel@tonic-gate 	flk.l_sysid = sysid;
8359*7c478bd9Sstevel@tonic-gate 	flk.l_pid = pid;
8360*7c478bd9Sstevel@tonic-gate 	flag |= F_REMOTELOCK;
8361*7c478bd9Sstevel@tonic-gate 
8362*7c478bd9Sstevel@tonic-gate 	LOCK_PRINT(rfs4_debug, "rfs4_op_lockt", F_GETLK, &flk);
8363*7c478bd9Sstevel@tonic-gate 
8364*7c478bd9Sstevel@tonic-gate 	/* Note that length4 is uint64_t but l_len and l_start are off64_t */
8365*7c478bd9Sstevel@tonic-gate 	if (flk.l_len < 0 || flk.l_start < 0) {
8366*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_INVAL;
8367*7c478bd9Sstevel@tonic-gate 		goto out;
8368*7c478bd9Sstevel@tonic-gate 	}
8369*7c478bd9Sstevel@tonic-gate 	error = VOP_FRLOCK(cs->vp, F_GETLK, &flk, flag, (u_offset_t)0,
8370*7c478bd9Sstevel@tonic-gate 	    NULL, cs->cr);
8371*7c478bd9Sstevel@tonic-gate 
8372*7c478bd9Sstevel@tonic-gate 	/*
8373*7c478bd9Sstevel@tonic-gate 	 * N.B. We map error values to nfsv4 errors. This is differrent
8374*7c478bd9Sstevel@tonic-gate 	 * than puterrno4 routine.
8375*7c478bd9Sstevel@tonic-gate 	 */
8376*7c478bd9Sstevel@tonic-gate 	switch (error) {
8377*7c478bd9Sstevel@tonic-gate 	case 0:
8378*7c478bd9Sstevel@tonic-gate 		if (flk.l_type == F_UNLCK)
8379*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4_OK;
8380*7c478bd9Sstevel@tonic-gate 		else {
8381*7c478bd9Sstevel@tonic-gate 			if (lock_denied(&resp->denied, &flk) == NFS4ERR_EXPIRED)
8382*7c478bd9Sstevel@tonic-gate 				goto retry;
8383*7c478bd9Sstevel@tonic-gate 			resp->status = NFS4ERR_DENIED;
8384*7c478bd9Sstevel@tonic-gate 		}
8385*7c478bd9Sstevel@tonic-gate 		break;
8386*7c478bd9Sstevel@tonic-gate 	case EOVERFLOW:
8387*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_INVAL;
8388*7c478bd9Sstevel@tonic-gate 		break;
8389*7c478bd9Sstevel@tonic-gate 	case EINVAL:
8390*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_NOTSUPP;
8391*7c478bd9Sstevel@tonic-gate 		break;
8392*7c478bd9Sstevel@tonic-gate 	default:
8393*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "rfs4_op_lockt: unexpected errno (%d)",
8394*7c478bd9Sstevel@tonic-gate 			error);
8395*7c478bd9Sstevel@tonic-gate 		resp->status = NFS4ERR_SERVERFAULT;
8396*7c478bd9Sstevel@tonic-gate 		break;
8397*7c478bd9Sstevel@tonic-gate 	}
8398*7c478bd9Sstevel@tonic-gate 
8399*7c478bd9Sstevel@tonic-gate out:
8400*7c478bd9Sstevel@tonic-gate 	if (lo)
8401*7c478bd9Sstevel@tonic-gate 		rfs4_lockowner_rele(lo);
8402*7c478bd9Sstevel@tonic-gate 	*cs->statusp = resp->status;
8403*7c478bd9Sstevel@tonic-gate }
8404*7c478bd9Sstevel@tonic-gate 
8405*7c478bd9Sstevel@tonic-gate static int
8406*7c478bd9Sstevel@tonic-gate vop_shrlock(vnode_t *vp, int cmd, struct shrlock *sp, int fflags)
8407*7c478bd9Sstevel@tonic-gate {
8408*7c478bd9Sstevel@tonic-gate 	int err;
8409*7c478bd9Sstevel@tonic-gate 
8410*7c478bd9Sstevel@tonic-gate 	if (cmd == F_UNSHARE && sp->s_deny == 0 && sp->s_access == 0)
8411*7c478bd9Sstevel@tonic-gate 		return (0);
8412*7c478bd9Sstevel@tonic-gate 
8413*7c478bd9Sstevel@tonic-gate 	err = VOP_SHRLOCK(vp, cmd, sp, fflags, CRED());
8414*7c478bd9Sstevel@tonic-gate 
8415*7c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(rfs4_shrlock_debug,
8416*7c478bd9Sstevel@tonic-gate 		(CE_NOTE, "rfs4_shrlock %s vp=%p acc=%d dny=%d sysid=%d "
8417*7c478bd9Sstevel@tonic-gate 		"pid=%d err=%d\n", cmd == F_SHARE ? "SHARE" : "UNSHR",
8418*7c478bd9Sstevel@tonic-gate 		(void *) vp, sp->s_access, sp->s_deny, sp->s_sysid, sp->s_pid,
8419*7c478bd9Sstevel@tonic-gate 		err));
8420*7c478bd9Sstevel@tonic-gate 
8421*7c478bd9Sstevel@tonic-gate 	return (err);
8422*7c478bd9Sstevel@tonic-gate }
8423*7c478bd9Sstevel@tonic-gate 
8424*7c478bd9Sstevel@tonic-gate static int
8425*7c478bd9Sstevel@tonic-gate rfs4_shrlock(rfs4_state_t *sp, int cmd)
8426*7c478bd9Sstevel@tonic-gate {
8427*7c478bd9Sstevel@tonic-gate 	struct shrlock shr;
8428*7c478bd9Sstevel@tonic-gate 	struct shr_locowner shr_loco;
8429*7c478bd9Sstevel@tonic-gate 	int fflags;
8430*7c478bd9Sstevel@tonic-gate 
8431*7c478bd9Sstevel@tonic-gate 	fflags = shr.s_access = shr.s_deny = 0;
8432*7c478bd9Sstevel@tonic-gate 
8433*7c478bd9Sstevel@tonic-gate 	if (sp->share_access & OPEN4_SHARE_ACCESS_READ) {
8434*7c478bd9Sstevel@tonic-gate 		fflags |= FREAD;
8435*7c478bd9Sstevel@tonic-gate 		shr.s_access |= F_RDACC;
8436*7c478bd9Sstevel@tonic-gate 	}
8437*7c478bd9Sstevel@tonic-gate 	if (sp->share_access & OPEN4_SHARE_ACCESS_WRITE) {
8438*7c478bd9Sstevel@tonic-gate 		fflags |= FWRITE;
8439*7c478bd9Sstevel@tonic-gate 		shr.s_access |= F_WRACC;
8440*7c478bd9Sstevel@tonic-gate 	}
8441*7c478bd9Sstevel@tonic-gate 	if (sp->share_deny & OPEN4_SHARE_DENY_READ)
8442*7c478bd9Sstevel@tonic-gate 		shr.s_deny |= F_RDDNY;
8443*7c478bd9Sstevel@tonic-gate 	if (sp->share_deny & OPEN4_SHARE_DENY_WRITE)
8444*7c478bd9Sstevel@tonic-gate 		shr.s_deny |= F_WRDNY;
8445*7c478bd9Sstevel@tonic-gate 
8446*7c478bd9Sstevel@tonic-gate 	shr.s_pid = rfs4_dbe_getid(sp->owner->dbe);
8447*7c478bd9Sstevel@tonic-gate 	shr.s_sysid = sp->owner->client->sysidt;
8448*7c478bd9Sstevel@tonic-gate 	shr_loco.sl_pid = shr.s_pid;
8449*7c478bd9Sstevel@tonic-gate 	shr_loco.sl_id = shr.s_sysid;
8450*7c478bd9Sstevel@tonic-gate 	shr.s_owner = (caddr_t)&shr_loco;
8451*7c478bd9Sstevel@tonic-gate 	shr.s_own_len = sizeof (shr_loco);
8452*7c478bd9Sstevel@tonic-gate 	return (vop_shrlock(sp->finfo->vp, cmd, &shr, fflags));
8453*7c478bd9Sstevel@tonic-gate }
8454*7c478bd9Sstevel@tonic-gate 
8455*7c478bd9Sstevel@tonic-gate static int
8456*7c478bd9Sstevel@tonic-gate rfs4_share(rfs4_state_t *sp)
8457*7c478bd9Sstevel@tonic-gate {
8458*7c478bd9Sstevel@tonic-gate 	return (rfs4_shrlock(sp, F_SHARE));
8459*7c478bd9Sstevel@tonic-gate }
8460*7c478bd9Sstevel@tonic-gate 
8461*7c478bd9Sstevel@tonic-gate void
8462*7c478bd9Sstevel@tonic-gate rfs4_unshare(rfs4_state_t *sp)
8463*7c478bd9Sstevel@tonic-gate {
8464*7c478bd9Sstevel@tonic-gate 	(void) rfs4_shrlock(sp, F_UNSHARE);
8465*7c478bd9Sstevel@tonic-gate }
8466