xref: /titanic_44/usr/src/uts/common/fs/nfs/nfs3_srv.c (revision d29f5a711240f866521445b1656d114da090335e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28 
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 #include <sys/cred.h>
33 #include <sys/buf.h>
34 #include <sys/vfs.h>
35 #include <sys/vnode.h>
36 #include <sys/uio.h>
37 #include <sys/errno.h>
38 #include <sys/sysmacros.h>
39 #include <sys/statvfs.h>
40 #include <sys/kmem.h>
41 #include <sys/dirent.h>
42 #include <sys/cmn_err.h>
43 #include <sys/debug.h>
44 #include <sys/systeminfo.h>
45 #include <sys/flock.h>
46 #include <sys/nbmlock.h>
47 #include <sys/policy.h>
48 #include <sys/sdt.h>
49 
50 #include <rpc/types.h>
51 #include <rpc/auth.h>
52 #include <rpc/svc.h>
53 #include <rpc/rpc_rdma.h>
54 
55 #include <nfs/nfs.h>
56 #include <nfs/export.h>
57 
58 #include <sys/strsubr.h>
59 
60 #include <sys/tsol/label.h>
61 #include <sys/tsol/tndb.h>
62 
63 #include <inet/ip.h>
64 #include <inet/ip6.h>
65 
66 /*
67  * These are the interface routines for the server side of the
68  * Network File System.  See the NFS version 3 protocol specification
69  * for a description of this interface.
70  */
71 
72 #ifdef DEBUG
73 int rfs3_do_pre_op_attr = 1;
74 int rfs3_do_post_op_attr = 1;
75 int rfs3_do_post_op_fh3 = 1;
76 #endif
77 
78 static writeverf3 write3verf;
79 
80 static int	sattr3_to_vattr(sattr3 *, struct vattr *);
81 static int	vattr_to_fattr3(struct vattr *, fattr3 *);
82 static int	vattr_to_wcc_attr(struct vattr *, wcc_attr *);
83 static void	vattr_to_pre_op_attr(struct vattr *, pre_op_attr *);
84 static void	vattr_to_wcc_data(struct vattr *, struct vattr *, wcc_data *);
85 static int	rdma_setup_read_data3(READ3args *, READ3resok *);
86 
87 u_longlong_t nfs3_srv_caller_id;
88 
89 /* ARGSUSED */
90 void
91 rfs3_getattr(GETATTR3args *args, GETATTR3res *resp, struct exportinfo *exi,
92 	struct svc_req *req, cred_t *cr)
93 {
94 	int error;
95 	vnode_t *vp;
96 	struct vattr va;
97 
98 	vp = nfs3_fhtovp(&args->object, exi);
99 
100 	DTRACE_NFSV3_4(op__getattr__start, struct svc_req *, req,
101 	    cred_t *, cr, vnode_t *, vp, GETATTR3args *, args);
102 
103 	if (vp == NULL) {
104 		error = ESTALE;
105 		goto out;
106 	}
107 
108 	va.va_mask = AT_ALL;
109 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
110 
111 	if (!error) {
112 		/* overflow error if time or size is out of range */
113 		error = vattr_to_fattr3(&va, &resp->resok.obj_attributes);
114 		if (error)
115 			goto out;
116 		resp->status = NFS3_OK;
117 
118 		DTRACE_NFSV3_4(op__getattr__done, struct svc_req *, req,
119 		    cred_t *, cr, vnode_t *, vp, GETATTR3res *, resp);
120 
121 		VN_RELE(vp);
122 
123 		return;
124 	}
125 
126 out:
127 	if (curthread->t_flag & T_WOULDBLOCK) {
128 		curthread->t_flag &= ~T_WOULDBLOCK;
129 		resp->status = NFS3ERR_JUKEBOX;
130 	} else
131 		resp->status = puterrno3(error);
132 
133 	DTRACE_NFSV3_4(op__getattr__done, struct svc_req *, req,
134 	    cred_t *, cr, vnode_t *, vp, GETATTR3res *, resp);
135 
136 	if (vp != NULL)
137 		VN_RELE(vp);
138 }
139 
140 void *
141 rfs3_getattr_getfh(GETATTR3args *args)
142 {
143 
144 	return (&args->object);
145 }
146 
147 void
148 rfs3_setattr(SETATTR3args *args, SETATTR3res *resp, struct exportinfo *exi,
149 	struct svc_req *req, cred_t *cr)
150 {
151 	int error;
152 	vnode_t *vp;
153 	struct vattr *bvap;
154 	struct vattr bva;
155 	struct vattr *avap;
156 	struct vattr ava;
157 	int flag;
158 	int in_crit = 0;
159 	struct flock64 bf;
160 	caller_context_t ct;
161 
162 	bvap = NULL;
163 	avap = NULL;
164 
165 	vp = nfs3_fhtovp(&args->object, exi);
166 
167 	DTRACE_NFSV3_4(op__setattr__start, struct svc_req *, req,
168 	    cred_t *, cr, vnode_t *, vp, SETATTR3args *, args);
169 
170 	if (vp == NULL) {
171 		error = ESTALE;
172 		goto out;
173 	}
174 
175 	error = sattr3_to_vattr(&args->new_attributes, &ava);
176 	if (error)
177 		goto out;
178 
179 	if (is_system_labeled()) {
180 		bslabel_t *clabel = req->rq_label;
181 
182 		ASSERT(clabel != NULL);
183 		DTRACE_PROBE2(tx__rfs3__log__info__opsetattr__clabel, char *,
184 		    "got client label from request(1)", struct svc_req *, req);
185 
186 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
187 			if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK)) {
188 				resp->status = NFS3ERR_ACCES;
189 				goto out1;
190 			}
191 		}
192 	}
193 
194 	/*
195 	 * We need to specially handle size changes because of
196 	 * possible conflicting NBMAND locks. Get into critical
197 	 * region before VOP_GETATTR, so the size attribute is
198 	 * valid when checking conflicts.
199 	 *
200 	 * Also, check to see if the v4 side of the server has
201 	 * delegated this file.  If so, then we return JUKEBOX to
202 	 * allow the client to retrasmit its request.
203 	 */
204 	if (vp->v_type == VREG && (ava.va_mask & AT_SIZE)) {
205 		if (nbl_need_check(vp)) {
206 			nbl_start_crit(vp, RW_READER);
207 			in_crit = 1;
208 		}
209 	}
210 
211 	bva.va_mask = AT_ALL;
212 	error = rfs4_delegated_getattr(vp, &bva, 0, cr);
213 
214 	/*
215 	 * If we can't get the attributes, then we can't do the
216 	 * right access checking.  So, we'll fail the request.
217 	 */
218 	if (error)
219 		goto out;
220 
221 #ifdef DEBUG
222 	if (rfs3_do_pre_op_attr)
223 		bvap = &bva;
224 #else
225 	bvap = &bva;
226 #endif
227 
228 	if (rdonly(exi, req) || vn_is_readonly(vp)) {
229 		resp->status = NFS3ERR_ROFS;
230 		goto out1;
231 	}
232 
233 	if (args->guard.check &&
234 	    (args->guard.obj_ctime.seconds != bva.va_ctime.tv_sec ||
235 	    args->guard.obj_ctime.nseconds != bva.va_ctime.tv_nsec)) {
236 		resp->status = NFS3ERR_NOT_SYNC;
237 		goto out1;
238 	}
239 
240 	if (args->new_attributes.mtime.set_it == SET_TO_CLIENT_TIME)
241 		flag = ATTR_UTIME;
242 	else
243 		flag = 0;
244 
245 	/*
246 	 * If the filesystem is exported with nosuid, then mask off
247 	 * the setuid and setgid bits.
248 	 */
249 	if ((ava.va_mask & AT_MODE) && vp->v_type == VREG &&
250 	    (exi->exi_export.ex_flags & EX_NOSUID))
251 		ava.va_mode &= ~(VSUID | VSGID);
252 
253 	ct.cc_sysid = 0;
254 	ct.cc_pid = 0;
255 	ct.cc_caller_id = nfs3_srv_caller_id;
256 	ct.cc_flags = CC_DONTBLOCK;
257 
258 	/*
259 	 * We need to specially handle size changes because it is
260 	 * possible for the client to create a file with modes
261 	 * which indicate read-only, but with the file opened for
262 	 * writing.  If the client then tries to set the size of
263 	 * the file, then the normal access checking done in
264 	 * VOP_SETATTR would prevent the client from doing so,
265 	 * although it should be legal for it to do so.  To get
266 	 * around this, we do the access checking for ourselves
267 	 * and then use VOP_SPACE which doesn't do the access
268 	 * checking which VOP_SETATTR does. VOP_SPACE can only
269 	 * operate on VREG files, let VOP_SETATTR handle the other
270 	 * extremely rare cases.
271 	 * Also the client should not be allowed to change the
272 	 * size of the file if there is a conflicting non-blocking
273 	 * mandatory lock in the region the change.
274 	 */
275 	if (vp->v_type == VREG && (ava.va_mask & AT_SIZE)) {
276 		if (in_crit) {
277 			u_offset_t offset;
278 			ssize_t length;
279 
280 			if (ava.va_size < bva.va_size) {
281 				offset = ava.va_size;
282 				length = bva.va_size - ava.va_size;
283 			} else {
284 				offset = bva.va_size;
285 				length = ava.va_size - bva.va_size;
286 			}
287 			if (nbl_conflict(vp, NBL_WRITE, offset, length, 0,
288 			    NULL)) {
289 				error = EACCES;
290 				goto out;
291 			}
292 		}
293 
294 		if (crgetuid(cr) == bva.va_uid && ava.va_size != bva.va_size) {
295 			ava.va_mask &= ~AT_SIZE;
296 			bf.l_type = F_WRLCK;
297 			bf.l_whence = 0;
298 			bf.l_start = (off64_t)ava.va_size;
299 			bf.l_len = 0;
300 			bf.l_sysid = 0;
301 			bf.l_pid = 0;
302 			error = VOP_SPACE(vp, F_FREESP, &bf, FWRITE,
303 			    (offset_t)ava.va_size, cr, &ct);
304 		}
305 	}
306 
307 	if (!error && ava.va_mask)
308 		error = VOP_SETATTR(vp, &ava, flag, cr, &ct);
309 
310 	/* check if a monitor detected a delegation conflict */
311 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
312 		resp->status = NFS3ERR_JUKEBOX;
313 		goto out1;
314 	}
315 
316 #ifdef DEBUG
317 	if (rfs3_do_post_op_attr) {
318 		ava.va_mask = AT_ALL;
319 		avap = rfs4_delegated_getattr(vp, &ava, 0, cr) ? NULL : &ava;
320 	} else
321 		avap = NULL;
322 #else
323 	ava.va_mask = AT_ALL;
324 	avap = rfs4_delegated_getattr(vp, &ava, 0, cr) ? NULL : &ava;
325 #endif
326 
327 	/*
328 	 * Force modified metadata out to stable storage.
329 	 */
330 	(void) VOP_FSYNC(vp, FNODSYNC, cr, &ct);
331 
332 	if (error)
333 		goto out;
334 
335 	if (in_crit)
336 		nbl_end_crit(vp);
337 
338 	resp->status = NFS3_OK;
339 	vattr_to_wcc_data(bvap, avap, &resp->resok.obj_wcc);
340 
341 	DTRACE_NFSV3_4(op__setattr__done, struct svc_req *, req,
342 	    cred_t *, cr, vnode_t *, vp, SETATTR3res *, resp);
343 
344 	VN_RELE(vp);
345 
346 	return;
347 
348 out:
349 	if (curthread->t_flag & T_WOULDBLOCK) {
350 		curthread->t_flag &= ~T_WOULDBLOCK;
351 		resp->status = NFS3ERR_JUKEBOX;
352 	} else
353 		resp->status = puterrno3(error);
354 out1:
355 	DTRACE_NFSV3_4(op__setattr__done, struct svc_req *, req,
356 	    cred_t *, cr, vnode_t *, vp, SETATTR3res *, resp);
357 
358 	if (vp != NULL) {
359 		if (in_crit)
360 			nbl_end_crit(vp);
361 		VN_RELE(vp);
362 	}
363 	vattr_to_wcc_data(bvap, avap, &resp->resfail.obj_wcc);
364 }
365 
366 void *
367 rfs3_setattr_getfh(SETATTR3args *args)
368 {
369 
370 	return (&args->object);
371 }
372 
373 /* ARGSUSED */
374 void
375 rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi,
376 	struct svc_req *req, cred_t *cr)
377 {
378 	int error;
379 	vnode_t *vp;
380 	vnode_t *dvp;
381 	struct vattr *vap;
382 	struct vattr va;
383 	struct vattr *dvap;
384 	struct vattr dva;
385 	nfs_fh3 *fhp;
386 	struct sec_ol sec = {0, 0};
387 	bool_t publicfh_flag = FALSE, auth_weak = FALSE;
388 
389 	dvap = NULL;
390 
391 	/*
392 	 * Allow lookups from the root - the default
393 	 * location of the public filehandle.
394 	 */
395 	if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
396 		dvp = rootdir;
397 		VN_HOLD(dvp);
398 
399 		DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
400 		    cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
401 	} else {
402 		dvp = nfs3_fhtovp(&args->what.dir, exi);
403 
404 		DTRACE_NFSV3_4(op__lookup__start, struct svc_req *, req,
405 		    cred_t *, cr, vnode_t *, dvp, LOOKUP3args *, args);
406 
407 		if (dvp == NULL) {
408 			error = ESTALE;
409 			goto out;
410 		}
411 	}
412 
413 #ifdef DEBUG
414 	if (rfs3_do_pre_op_attr) {
415 		dva.va_mask = AT_ALL;
416 		dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
417 	}
418 #else
419 	dva.va_mask = AT_ALL;
420 	dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
421 #endif
422 
423 	if (args->what.name == nfs3nametoolong) {
424 		resp->status = NFS3ERR_NAMETOOLONG;
425 		goto out1;
426 	}
427 
428 	if (args->what.name == NULL || *(args->what.name) == '\0') {
429 		resp->status = NFS3ERR_ACCES;
430 		goto out1;
431 	}
432 
433 	fhp = &args->what.dir;
434 	if (strcmp(args->what.name, "..") == 0 &&
435 	    EQFID(&exi->exi_fid, FH3TOFIDP(fhp))) {
436 		resp->status = NFS3ERR_NOENT;
437 		goto out1;
438 	}
439 
440 	/*
441 	 * If the public filehandle is used then allow
442 	 * a multi-component lookup
443 	 */
444 	if (PUBLIC_FH3(&args->what.dir)) {
445 		publicfh_flag = TRUE;
446 		error = rfs_publicfh_mclookup(args->what.name, dvp, cr, &vp,
447 		    &exi, &sec);
448 		if (error && exi != NULL)
449 			exi_rele(exi); /* See comment below Re: publicfh_flag */
450 		/*
451 		 * Since WebNFS may bypass MOUNT, we need to ensure this
452 		 * request didn't come from an unlabeled admin_low client.
453 		 */
454 		if (is_system_labeled() && error == 0) {
455 			struct sockaddr *ca;
456 			int		addr_type;
457 			void		*ipaddr;
458 			tsol_tpc_t	*tp;
459 
460 			ca = (struct sockaddr *)svc_getrpccaller(
461 			    req->rq_xprt)->buf;
462 			if (ca->sa_family == AF_INET) {
463 				addr_type = IPV4_VERSION;
464 				ipaddr = &((struct sockaddr_in *)ca)->sin_addr;
465 			} else if (ca->sa_family == AF_INET6) {
466 				addr_type = IPV6_VERSION;
467 				ipaddr = &((struct sockaddr_in6 *)
468 				    ca)->sin6_addr;
469 			}
470 			tp = find_tpc(ipaddr, addr_type, B_FALSE);
471 			if (tp == NULL || tp->tpc_tp.tp_doi !=
472 			    l_admin_low->tsl_doi || tp->tpc_tp.host_type !=
473 			    SUN_CIPSO) {
474 				if (exi != NULL)
475 					exi_rele(exi);
476 				VN_RELE(vp);
477 				resp->status = NFS3ERR_ACCES;
478 				error = 1;
479 			}
480 			if (tp != NULL)
481 				TPC_RELE(tp);
482 		}
483 	} else {
484 		error = VOP_LOOKUP(dvp, args->what.name, &vp,
485 		    NULL, 0, NULL, cr, NULL, NULL, NULL);
486 	}
487 
488 	if (is_system_labeled() && error == 0) {
489 		bslabel_t *clabel = req->rq_label;
490 
491 		ASSERT(clabel != NULL);
492 		DTRACE_PROBE2(tx__rfs3__log__info__oplookup__clabel, char *,
493 		    "got client label from request(1)", struct svc_req *, req);
494 
495 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
496 			if (!do_rfs_label_check(clabel, dvp,
497 			    DOMINANCE_CHECK)) {
498 				if (publicfh_flag && exi != NULL)
499 					exi_rele(exi);
500 				VN_RELE(vp);
501 				resp->status = NFS3ERR_ACCES;
502 				error = 1;
503 			}
504 		}
505 	}
506 
507 #ifdef DEBUG
508 	if (rfs3_do_post_op_attr) {
509 		dva.va_mask = AT_ALL;
510 		dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
511 	} else
512 		dvap = NULL;
513 #else
514 	dva.va_mask = AT_ALL;
515 	dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva;
516 #endif
517 
518 	if (error)
519 		goto out;
520 
521 	if (sec.sec_flags & SEC_QUERY) {
522 		error = makefh3_ol(&resp->resok.object, exi, sec.sec_index);
523 	} else {
524 		error = makefh3(&resp->resok.object, vp, exi);
525 		if (!error && publicfh_flag && !chk_clnt_sec(exi, req))
526 			auth_weak = TRUE;
527 	}
528 
529 	if (error) {
530 		VN_RELE(vp);
531 		goto out;
532 	}
533 
534 	/*
535 	 * If publicfh_flag is true then we have called rfs_publicfh_mclookup
536 	 * and have obtained a new exportinfo in exi which needs to be
537 	 * released. Note the the original exportinfo pointed to by exi
538 	 * will be released by the caller, common_dispatch.
539 	 */
540 	if (publicfh_flag)
541 		exi_rele(exi);
542 
543 #ifdef DEBUG
544 	if (rfs3_do_post_op_attr) {
545 		va.va_mask = AT_ALL;
546 		vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
547 	} else
548 		vap = NULL;
549 #else
550 	va.va_mask = AT_ALL;
551 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
552 #endif
553 
554 	VN_RELE(vp);
555 
556 	resp->status = NFS3_OK;
557 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
558 	vattr_to_post_op_attr(dvap, &resp->resok.dir_attributes);
559 
560 	/*
561 	 * If it's public fh, no 0x81, and client's flavor is
562 	 * invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.
563 	 * Then set RPC status to AUTH_TOOWEAK in common_dispatch.
564 	 */
565 	if (auth_weak)
566 		resp->status = (enum nfsstat3)WNFSERR_CLNT_FLAVOR;
567 
568 	DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
569 	    cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
570 	VN_RELE(dvp);
571 
572 	return;
573 
574 out:
575 	if (curthread->t_flag & T_WOULDBLOCK) {
576 		curthread->t_flag &= ~T_WOULDBLOCK;
577 		resp->status = NFS3ERR_JUKEBOX;
578 	} else
579 		resp->status = puterrno3(error);
580 out1:
581 	DTRACE_NFSV3_4(op__lookup__done, struct svc_req *, req,
582 	    cred_t *, cr, vnode_t *, dvp, LOOKUP3res *, resp);
583 
584 	if (dvp != NULL)
585 		VN_RELE(dvp);
586 	vattr_to_post_op_attr(dvap, &resp->resfail.dir_attributes);
587 
588 }
589 
590 void *
591 rfs3_lookup_getfh(LOOKUP3args *args)
592 {
593 
594 	return (&args->what.dir);
595 }
596 
597 /* ARGSUSED */
598 void
599 rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi,
600 	struct svc_req *req, cred_t *cr)
601 {
602 	int error;
603 	vnode_t *vp;
604 	struct vattr *vap;
605 	struct vattr va;
606 	int checkwriteperm;
607 	boolean_t dominant_label = B_FALSE;
608 	boolean_t equal_label = B_FALSE;
609 	boolean_t admin_low_client;
610 
611 	vap = NULL;
612 
613 	vp = nfs3_fhtovp(&args->object, exi);
614 
615 	DTRACE_NFSV3_4(op__access__start, struct svc_req *, req,
616 	    cred_t *, cr, vnode_t *, vp, ACCESS3args *, args);
617 
618 	if (vp == NULL) {
619 		error = ESTALE;
620 		goto out;
621 	}
622 
623 	/*
624 	 * If the file system is exported read only, it is not appropriate
625 	 * to check write permissions for regular files and directories.
626 	 * Special files are interpreted by the client, so the underlying
627 	 * permissions are sent back to the client for interpretation.
628 	 */
629 	if (rdonly(exi, req) && (vp->v_type == VREG || vp->v_type == VDIR))
630 		checkwriteperm = 0;
631 	else
632 		checkwriteperm = 1;
633 
634 	/*
635 	 * We need the mode so that we can correctly determine access
636 	 * permissions relative to a mandatory lock file.  Access to
637 	 * mandatory lock files is denied on the server, so it might
638 	 * as well be reflected to the server during the open.
639 	 */
640 	va.va_mask = AT_MODE;
641 	error = VOP_GETATTR(vp, &va, 0, cr, NULL);
642 	if (error)
643 		goto out;
644 
645 #ifdef DEBUG
646 	if (rfs3_do_post_op_attr)
647 		vap = &va;
648 #else
649 	vap = &va;
650 #endif
651 
652 	resp->resok.access = 0;
653 
654 	if (is_system_labeled()) {
655 		bslabel_t *clabel = req->rq_label;
656 
657 		ASSERT(clabel != NULL);
658 		DTRACE_PROBE2(tx__rfs3__log__info__opaccess__clabel, char *,
659 		    "got client label from request(1)", struct svc_req *, req);
660 
661 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
662 			if ((equal_label = do_rfs_label_check(clabel, vp,
663 			    EQUALITY_CHECK)) == B_FALSE) {
664 				dominant_label = do_rfs_label_check(clabel,
665 				    vp, DOMINANCE_CHECK);
666 			} else
667 				dominant_label = B_TRUE;
668 			admin_low_client = B_FALSE;
669 		} else
670 			admin_low_client = B_TRUE;
671 	}
672 
673 	if (args->access & ACCESS3_READ) {
674 		error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
675 		if (error) {
676 			if (curthread->t_flag & T_WOULDBLOCK)
677 				goto out;
678 		} else if (!MANDLOCK(vp, va.va_mode) &&
679 		    (!is_system_labeled() || admin_low_client ||
680 		    dominant_label))
681 			resp->resok.access |= ACCESS3_READ;
682 	}
683 	if ((args->access & ACCESS3_LOOKUP) && vp->v_type == VDIR) {
684 		error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL);
685 		if (error) {
686 			if (curthread->t_flag & T_WOULDBLOCK)
687 				goto out;
688 		} else if (!is_system_labeled() || admin_low_client ||
689 		    dominant_label)
690 			resp->resok.access |= ACCESS3_LOOKUP;
691 	}
692 	if (checkwriteperm &&
693 	    (args->access & (ACCESS3_MODIFY|ACCESS3_EXTEND))) {
694 		error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL);
695 		if (error) {
696 			if (curthread->t_flag & T_WOULDBLOCK)
697 				goto out;
698 		} else if (!MANDLOCK(vp, va.va_mode) &&
699 		    (!is_system_labeled() || admin_low_client || equal_label)) {
700 			resp->resok.access |=
701 			    (args->access & (ACCESS3_MODIFY|ACCESS3_EXTEND));
702 		}
703 	}
704 	if (checkwriteperm &&
705 	    (args->access & ACCESS3_DELETE) && vp->v_type == VDIR) {
706 		error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL);
707 		if (error) {
708 			if (curthread->t_flag & T_WOULDBLOCK)
709 				goto out;
710 		} else if (!is_system_labeled() || admin_low_client ||
711 		    equal_label)
712 			resp->resok.access |= ACCESS3_DELETE;
713 	}
714 	if (args->access & ACCESS3_EXECUTE) {
715 		error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL);
716 		if (error) {
717 			if (curthread->t_flag & T_WOULDBLOCK)
718 				goto out;
719 		} else if (!MANDLOCK(vp, va.va_mode) &&
720 		    (!is_system_labeled() || admin_low_client ||
721 		    dominant_label))
722 			resp->resok.access |= ACCESS3_EXECUTE;
723 	}
724 
725 #ifdef DEBUG
726 	if (rfs3_do_post_op_attr) {
727 		va.va_mask = AT_ALL;
728 		vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
729 	} else
730 		vap = NULL;
731 #else
732 	va.va_mask = AT_ALL;
733 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
734 #endif
735 
736 	resp->status = NFS3_OK;
737 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
738 
739 	DTRACE_NFSV3_4(op__access__done, struct svc_req *, req,
740 	    cred_t *, cr, vnode_t *, vp, ACCESS3res *, resp);
741 
742 	VN_RELE(vp);
743 
744 	return;
745 
746 out:
747 	if (curthread->t_flag & T_WOULDBLOCK) {
748 		curthread->t_flag &= ~T_WOULDBLOCK;
749 		resp->status = NFS3ERR_JUKEBOX;
750 	} else
751 		resp->status = puterrno3(error);
752 	DTRACE_NFSV3_4(op__access__done, struct svc_req *, req,
753 	    cred_t *, cr, vnode_t *, vp, ACCESS3res *, resp);
754 	if (vp != NULL)
755 		VN_RELE(vp);
756 	vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
757 }
758 
759 void *
760 rfs3_access_getfh(ACCESS3args *args)
761 {
762 
763 	return (&args->object);
764 }
765 
766 /* ARGSUSED */
767 void
768 rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi,
769 	struct svc_req *req, cred_t *cr)
770 {
771 	int error;
772 	vnode_t *vp;
773 	struct vattr *vap;
774 	struct vattr va;
775 	struct iovec iov;
776 	struct uio uio;
777 	char *data;
778 
779 	vap = NULL;
780 
781 	vp = nfs3_fhtovp(&args->symlink, exi);
782 
783 	DTRACE_NFSV3_4(op__readlink__start, struct svc_req *, req,
784 	    cred_t *, cr, vnode_t *, vp, READLINK3args *, args);
785 
786 	if (vp == NULL) {
787 		error = ESTALE;
788 		goto out;
789 	}
790 
791 	va.va_mask = AT_ALL;
792 	error = VOP_GETATTR(vp, &va, 0, cr, NULL);
793 	if (error)
794 		goto out;
795 
796 #ifdef DEBUG
797 	if (rfs3_do_post_op_attr)
798 		vap = &va;
799 #else
800 	vap = &va;
801 #endif
802 
803 	if (vp->v_type != VLNK) {
804 		resp->status = NFS3ERR_INVAL;
805 		goto out1;
806 	}
807 
808 	if (MANDLOCK(vp, va.va_mode)) {
809 		resp->status = NFS3ERR_ACCES;
810 		goto out1;
811 	}
812 
813 	if (is_system_labeled()) {
814 		bslabel_t *clabel = req->rq_label;
815 
816 		ASSERT(clabel != NULL);
817 		DTRACE_PROBE2(tx__rfs3__log__info__opreadlink__clabel, char *,
818 		    "got client label from request(1)", struct svc_req *, req);
819 
820 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
821 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
822 				resp->status = NFS3ERR_ACCES;
823 				goto out1;
824 			}
825 		}
826 	}
827 
828 	data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP);
829 
830 	iov.iov_base = data;
831 	iov.iov_len = MAXPATHLEN;
832 	uio.uio_iov = &iov;
833 	uio.uio_iovcnt = 1;
834 	uio.uio_segflg = UIO_SYSSPACE;
835 	uio.uio_extflg = UIO_COPY_CACHED;
836 	uio.uio_loffset = 0;
837 	uio.uio_resid = MAXPATHLEN;
838 
839 	error = VOP_READLINK(vp, &uio, cr, NULL);
840 
841 #ifdef DEBUG
842 	if (rfs3_do_post_op_attr) {
843 		va.va_mask = AT_ALL;
844 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
845 	} else
846 		vap = NULL;
847 #else
848 	va.va_mask = AT_ALL;
849 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
850 #endif
851 
852 #if 0 /* notyet */
853 	/*
854 	 * Don't do this.  It causes local disk writes when just
855 	 * reading the file and the overhead is deemed larger
856 	 * than the benefit.
857 	 */
858 	/*
859 	 * Force modified metadata out to stable storage.
860 	 */
861 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
862 #endif
863 
864 	if (error) {
865 		kmem_free(data, MAXPATHLEN + 1);
866 		goto out;
867 	}
868 
869 	resp->status = NFS3_OK;
870 	vattr_to_post_op_attr(vap, &resp->resok.symlink_attributes);
871 	resp->resok.data = data;
872 	*(data + MAXPATHLEN - uio.uio_resid) = '\0';
873 
874 	DTRACE_NFSV3_4(op__readlink__done, struct svc_req *, req,
875 	    cred_t *, cr, vnode_t *, vp, READLINK3res *, resp);
876 	VN_RELE(vp);
877 
878 	return;
879 
880 out:
881 	if (curthread->t_flag & T_WOULDBLOCK) {
882 		curthread->t_flag &= ~T_WOULDBLOCK;
883 		resp->status = NFS3ERR_JUKEBOX;
884 	} else
885 		resp->status = puterrno3(error);
886 out1:
887 	DTRACE_NFSV3_4(op__readlink__done, struct svc_req *, req,
888 	    cred_t *, cr, vnode_t *, vp, READLINK3res *, resp);
889 	if (vp != NULL)
890 		VN_RELE(vp);
891 	vattr_to_post_op_attr(vap, &resp->resfail.symlink_attributes);
892 }
893 
894 void *
895 rfs3_readlink_getfh(READLINK3args *args)
896 {
897 
898 	return (&args->symlink);
899 }
900 
901 void
902 rfs3_readlink_free(READLINK3res *resp)
903 {
904 
905 	if (resp->status == NFS3_OK)
906 		kmem_free(resp->resok.data, MAXPATHLEN + 1);
907 }
908 
909 /*
910  * Server routine to handle read
911  * May handle RDMA data as well as mblks
912  */
913 /* ARGSUSED */
914 void
915 rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi,
916 	struct svc_req *req, cred_t *cr)
917 {
918 	int error;
919 	vnode_t *vp;
920 	struct vattr *vap;
921 	struct vattr va;
922 	struct iovec iov;
923 	struct uio uio;
924 	u_offset_t offset;
925 	mblk_t *mp;
926 	int alloc_err = 0;
927 	int in_crit = 0;
928 	int need_rwunlock = 0;
929 	caller_context_t ct;
930 
931 	vap = NULL;
932 
933 	vp = nfs3_fhtovp(&args->file, exi);
934 
935 	DTRACE_NFSV3_4(op__read__start, struct svc_req *, req,
936 	    cred_t *, cr, vnode_t *, vp, READ3args *, args);
937 
938 	if (vp == NULL) {
939 		error = ESTALE;
940 		goto out;
941 	}
942 
943 	if (is_system_labeled()) {
944 		bslabel_t *clabel = req->rq_label;
945 
946 		ASSERT(clabel != NULL);
947 		DTRACE_PROBE2(tx__rfs3__log__info__opread__clabel, char *,
948 		    "got client label from request(1)", struct svc_req *, req);
949 
950 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
951 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
952 				resp->status = NFS3ERR_ACCES;
953 				goto out1;
954 			}
955 		}
956 	}
957 
958 	ct.cc_sysid = 0;
959 	ct.cc_pid = 0;
960 	ct.cc_caller_id = nfs3_srv_caller_id;
961 	ct.cc_flags = CC_DONTBLOCK;
962 
963 	/*
964 	 * Enter the critical region before calling VOP_RWLOCK
965 	 * to avoid a deadlock with write requests.
966 	 */
967 	if (nbl_need_check(vp)) {
968 		nbl_start_crit(vp, RW_READER);
969 		in_crit = 1;
970 		if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0,
971 		    NULL)) {
972 			error = EACCES;
973 			goto out;
974 		}
975 	}
976 
977 	error = VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &ct);
978 
979 	/* check if a monitor detected a delegation conflict */
980 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
981 		resp->status = NFS3ERR_JUKEBOX;
982 		goto out1;
983 	}
984 
985 	need_rwunlock = 1;
986 
987 	va.va_mask = AT_ALL;
988 	error = VOP_GETATTR(vp, &va, 0, cr, &ct);
989 
990 	/*
991 	 * If we can't get the attributes, then we can't do the
992 	 * right access checking.  So, we'll fail the request.
993 	 */
994 	if (error)
995 		goto out;
996 
997 #ifdef DEBUG
998 	if (rfs3_do_post_op_attr)
999 		vap = &va;
1000 #else
1001 	vap = &va;
1002 #endif
1003 
1004 	if (vp->v_type != VREG) {
1005 		resp->status = NFS3ERR_INVAL;
1006 		goto out1;
1007 	}
1008 
1009 	if (crgetuid(cr) != va.va_uid) {
1010 		error = VOP_ACCESS(vp, VREAD, 0, cr, &ct);
1011 		if (error) {
1012 			if (curthread->t_flag & T_WOULDBLOCK)
1013 				goto out;
1014 			error = VOP_ACCESS(vp, VEXEC, 0, cr, &ct);
1015 			if (error)
1016 				goto out;
1017 		}
1018 	}
1019 
1020 	if (MANDLOCK(vp, va.va_mode)) {
1021 		resp->status = NFS3ERR_ACCES;
1022 		goto out1;
1023 	}
1024 
1025 	offset = args->offset;
1026 	if (offset >= va.va_size) {
1027 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1028 		if (in_crit)
1029 			nbl_end_crit(vp);
1030 		resp->status = NFS3_OK;
1031 		vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
1032 		resp->resok.count = 0;
1033 		resp->resok.eof = TRUE;
1034 		resp->resok.data.data_len = 0;
1035 		resp->resok.data.data_val = NULL;
1036 		resp->resok.data.mp = NULL;
1037 		/* RDMA */
1038 		resp->resok.wlist = args->wlist;
1039 		resp->resok.wlist_len = resp->resok.count;
1040 		goto done;
1041 	}
1042 
1043 	if (args->count == 0) {
1044 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1045 		if (in_crit)
1046 			nbl_end_crit(vp);
1047 		resp->status = NFS3_OK;
1048 		vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
1049 		resp->resok.count = 0;
1050 		resp->resok.eof = FALSE;
1051 		resp->resok.data.data_len = 0;
1052 		resp->resok.data.data_val = NULL;
1053 		resp->resok.data.mp = NULL;
1054 		/* RDMA */
1055 		resp->resok.wlist = args->wlist;
1056 		resp->resok.wlist_len = resp->resok.count;
1057 		goto done;
1058 	}
1059 
1060 	/*
1061 	 * do not allocate memory more the max. allowed
1062 	 * transfer size
1063 	 */
1064 	if (args->count > rfs3_tsize(req))
1065 		args->count = rfs3_tsize(req);
1066 
1067 	/*
1068 	 * If returning data via RDMA Write, then grab the chunk list.
1069 	 * If we aren't returning READ data w/RDMA_WRITE, then grab
1070 	 * a mblk.
1071 	 */
1072 	if (args->wlist) {
1073 		mp = NULL;
1074 		(void) rdma_get_wchunk(req, &iov, args->wlist);
1075 	} else {
1076 		/*
1077 		 * mp will contain the data to be sent out in the read reply.
1078 		 * This will be freed after the reply has been sent out (by the
1079 		 * driver).
1080 		 * Let's roundup the data to a BYTES_PER_XDR_UNIT multiple, so
1081 		 * that the call to xdrmblk_putmblk() never fails.
1082 		 */
1083 		mp = allocb_wait(RNDUP(args->count), BPRI_MED, STR_NOSIG,
1084 		    &alloc_err);
1085 		ASSERT(mp != NULL);
1086 		ASSERT(alloc_err == 0);
1087 
1088 		iov.iov_base = (caddr_t)mp->b_datap->db_base;
1089 		iov.iov_len = args->count;
1090 	}
1091 
1092 	uio.uio_iov = &iov;
1093 	uio.uio_iovcnt = 1;
1094 	uio.uio_segflg = UIO_SYSSPACE;
1095 	uio.uio_extflg = UIO_COPY_CACHED;
1096 	uio.uio_loffset = args->offset;
1097 	uio.uio_resid = args->count;
1098 
1099 	error = VOP_READ(vp, &uio, 0, cr, &ct);
1100 
1101 	if (error) {
1102 		freeb(mp);
1103 		/* check if a monitor detected a delegation conflict */
1104 		if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1105 			resp->status = NFS3ERR_JUKEBOX;
1106 			goto out1;
1107 		}
1108 		goto out;
1109 	}
1110 
1111 	va.va_mask = AT_ALL;
1112 	error = VOP_GETATTR(vp, &va, 0, cr, &ct);
1113 
1114 #ifdef DEBUG
1115 	if (rfs3_do_post_op_attr) {
1116 		if (error)
1117 			vap = NULL;
1118 		else
1119 			vap = &va;
1120 	} else
1121 		vap = NULL;
1122 #else
1123 	if (error)
1124 		vap = NULL;
1125 	else
1126 		vap = &va;
1127 #endif
1128 
1129 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1130 
1131 	if (in_crit)
1132 		nbl_end_crit(vp);
1133 
1134 	resp->status = NFS3_OK;
1135 	vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
1136 	resp->resok.count = args->count - uio.uio_resid;
1137 	if (!error && offset + resp->resok.count == va.va_size)
1138 		resp->resok.eof = TRUE;
1139 	else
1140 		resp->resok.eof = FALSE;
1141 	resp->resok.data.data_len = resp->resok.count;
1142 	resp->resok.data.mp = mp;
1143 	resp->resok.size = (uint_t)args->count;
1144 
1145 	if (args->wlist) {
1146 		resp->resok.data.data_val = (caddr_t)iov.iov_base;
1147 		if (!rdma_setup_read_data3(args, &(resp->resok))) {
1148 			resp->status = NFS3ERR_INVAL;
1149 		}
1150 	} else {
1151 		resp->resok.data.data_val = (caddr_t)mp->b_datap->db_base;
1152 		(resp->resok).wlist = NULL;
1153 	}
1154 
1155 done:
1156 	DTRACE_NFSV3_4(op__read__done, struct svc_req *, req,
1157 	    cred_t *, cr, vnode_t *, vp, READ3res *, resp);
1158 
1159 	VN_RELE(vp);
1160 
1161 	return;
1162 
1163 out:
1164 	if (curthread->t_flag & T_WOULDBLOCK) {
1165 		curthread->t_flag &= ~T_WOULDBLOCK;
1166 		resp->status = NFS3ERR_JUKEBOX;
1167 	} else
1168 		resp->status = puterrno3(error);
1169 out1:
1170 	DTRACE_NFSV3_4(op__read__done, struct svc_req *, req,
1171 	    cred_t *, cr, vnode_t *, vp, READ3res *, resp);
1172 
1173 	if (vp != NULL) {
1174 		if (need_rwunlock)
1175 			VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
1176 		if (in_crit)
1177 			nbl_end_crit(vp);
1178 		VN_RELE(vp);
1179 	}
1180 	vattr_to_post_op_attr(vap, &resp->resfail.file_attributes);
1181 }
1182 
1183 void
1184 rfs3_read_free(READ3res *resp)
1185 {
1186 	mblk_t *mp;
1187 
1188 	if (resp->status == NFS3_OK) {
1189 		mp = resp->resok.data.mp;
1190 		if (mp != NULL)
1191 			freeb(mp);
1192 	}
1193 }
1194 
1195 void *
1196 rfs3_read_getfh(READ3args *args)
1197 {
1198 
1199 	return (&args->file);
1200 }
1201 
1202 #define	MAX_IOVECS	12
1203 
1204 #ifdef DEBUG
1205 static int rfs3_write_hits = 0;
1206 static int rfs3_write_misses = 0;
1207 #endif
1208 
1209 void
1210 rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi,
1211 	struct svc_req *req, cred_t *cr)
1212 {
1213 	int error;
1214 	vnode_t *vp;
1215 	struct vattr *bvap = NULL;
1216 	struct vattr bva;
1217 	struct vattr *avap = NULL;
1218 	struct vattr ava;
1219 	u_offset_t rlimit;
1220 	struct uio uio;
1221 	struct iovec iov[MAX_IOVECS];
1222 	mblk_t *m;
1223 	struct iovec *iovp;
1224 	int iovcnt;
1225 	int ioflag;
1226 	cred_t *savecred;
1227 	int in_crit = 0;
1228 	int rwlock_ret = -1;
1229 	caller_context_t ct;
1230 
1231 	vp = nfs3_fhtovp(&args->file, exi);
1232 
1233 	DTRACE_NFSV3_4(op__write__start, struct svc_req *, req,
1234 	    cred_t *, cr, vnode_t *, vp, WRITE3args *, args);
1235 
1236 	if (vp == NULL) {
1237 		error = ESTALE;
1238 		goto err;
1239 	}
1240 
1241 	if (is_system_labeled()) {
1242 		bslabel_t *clabel = req->rq_label;
1243 
1244 		ASSERT(clabel != NULL);
1245 		DTRACE_PROBE2(tx__rfs3__log__info__opwrite__clabel, char *,
1246 		    "got client label from request(1)", struct svc_req *, req);
1247 
1248 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
1249 			if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK)) {
1250 				resp->status = NFS3ERR_ACCES;
1251 				goto err1;
1252 			}
1253 		}
1254 	}
1255 
1256 	ct.cc_sysid = 0;
1257 	ct.cc_pid = 0;
1258 	ct.cc_caller_id = nfs3_srv_caller_id;
1259 	ct.cc_flags = CC_DONTBLOCK;
1260 
1261 	/*
1262 	 * We have to enter the critical region before calling VOP_RWLOCK
1263 	 * to avoid a deadlock with ufs.
1264 	 */
1265 	if (nbl_need_check(vp)) {
1266 		nbl_start_crit(vp, RW_READER);
1267 		in_crit = 1;
1268 		if (nbl_conflict(vp, NBL_WRITE, args->offset, args->count, 0,
1269 		    NULL)) {
1270 			error = EACCES;
1271 			goto err;
1272 		}
1273 	}
1274 
1275 	rwlock_ret = VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &ct);
1276 
1277 	/* check if a monitor detected a delegation conflict */
1278 	if (rwlock_ret == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1279 		resp->status = NFS3ERR_JUKEBOX;
1280 		rwlock_ret = -1;
1281 		goto err1;
1282 	}
1283 
1284 
1285 	bva.va_mask = AT_ALL;
1286 	error = VOP_GETATTR(vp, &bva, 0, cr, &ct);
1287 
1288 	/*
1289 	 * If we can't get the attributes, then we can't do the
1290 	 * right access checking.  So, we'll fail the request.
1291 	 */
1292 	if (error)
1293 		goto err;
1294 
1295 	bvap = &bva;
1296 #ifdef DEBUG
1297 	if (!rfs3_do_pre_op_attr)
1298 		bvap = NULL;
1299 #endif
1300 	avap = bvap;
1301 
1302 	if (args->count != args->data.data_len) {
1303 		resp->status = NFS3ERR_INVAL;
1304 		goto err1;
1305 	}
1306 
1307 	if (rdonly(exi, req)) {
1308 		resp->status = NFS3ERR_ROFS;
1309 		goto err1;
1310 	}
1311 
1312 	if (vp->v_type != VREG) {
1313 		resp->status = NFS3ERR_INVAL;
1314 		goto err1;
1315 	}
1316 
1317 	if (crgetuid(cr) != bva.va_uid &&
1318 	    (error = VOP_ACCESS(vp, VWRITE, 0, cr, &ct)))
1319 		goto err;
1320 
1321 	if (MANDLOCK(vp, bva.va_mode)) {
1322 		resp->status = NFS3ERR_ACCES;
1323 		goto err1;
1324 	}
1325 
1326 	if (args->count == 0) {
1327 		resp->status = NFS3_OK;
1328 		vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
1329 		resp->resok.count = 0;
1330 		resp->resok.committed = args->stable;
1331 		resp->resok.verf = write3verf;
1332 		goto out;
1333 	}
1334 
1335 	if (args->mblk != NULL) {
1336 		iovcnt = 0;
1337 		for (m = args->mblk; m != NULL; m = m->b_cont)
1338 			iovcnt++;
1339 		if (iovcnt <= MAX_IOVECS) {
1340 #ifdef DEBUG
1341 			rfs3_write_hits++;
1342 #endif
1343 			iovp = iov;
1344 		} else {
1345 #ifdef DEBUG
1346 			rfs3_write_misses++;
1347 #endif
1348 			iovp = kmem_alloc(sizeof (*iovp) * iovcnt, KM_SLEEP);
1349 		}
1350 		mblk_to_iov(args->mblk, iovcnt, iovp);
1351 
1352 	} else if (args->rlist != NULL) {
1353 		iovcnt = 1;
1354 		iovp = iov;
1355 		iovp->iov_base = (char *)((args->rlist)->u.c_daddr3);
1356 		iovp->iov_len = args->count;
1357 	} else {
1358 		iovcnt = 1;
1359 		iovp = iov;
1360 		iovp->iov_base = args->data.data_val;
1361 		iovp->iov_len = args->count;
1362 	}
1363 
1364 	uio.uio_iov = iovp;
1365 	uio.uio_iovcnt = iovcnt;
1366 
1367 	uio.uio_segflg = UIO_SYSSPACE;
1368 	uio.uio_extflg = UIO_COPY_DEFAULT;
1369 	uio.uio_loffset = args->offset;
1370 	uio.uio_resid = args->count;
1371 	uio.uio_llimit = curproc->p_fsz_ctl;
1372 	rlimit = uio.uio_llimit - args->offset;
1373 	if (rlimit < (u_offset_t)uio.uio_resid)
1374 		uio.uio_resid = (int)rlimit;
1375 
1376 	if (args->stable == UNSTABLE)
1377 		ioflag = 0;
1378 	else if (args->stable == FILE_SYNC)
1379 		ioflag = FSYNC;
1380 	else if (args->stable == DATA_SYNC)
1381 		ioflag = FDSYNC;
1382 	else {
1383 		if (iovp != iov)
1384 			kmem_free(iovp, sizeof (*iovp) * iovcnt);
1385 		resp->status = NFS3ERR_INVAL;
1386 		goto err1;
1387 	}
1388 
1389 	/*
1390 	 * We're changing creds because VM may fault and we need
1391 	 * the cred of the current thread to be used if quota
1392 	 * checking is enabled.
1393 	 */
1394 	savecred = curthread->t_cred;
1395 	curthread->t_cred = cr;
1396 	error = VOP_WRITE(vp, &uio, ioflag, cr, &ct);
1397 	curthread->t_cred = savecred;
1398 
1399 	if (iovp != iov)
1400 		kmem_free(iovp, sizeof (*iovp) * iovcnt);
1401 
1402 	/* check if a monitor detected a delegation conflict */
1403 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
1404 		resp->status = NFS3ERR_JUKEBOX;
1405 		goto err1;
1406 	}
1407 
1408 	ava.va_mask = AT_ALL;
1409 	avap = VOP_GETATTR(vp, &ava, 0, cr, &ct) ? NULL : &ava;
1410 
1411 #ifdef DEBUG
1412 	if (!rfs3_do_post_op_attr)
1413 		avap = NULL;
1414 #endif
1415 
1416 	if (error)
1417 		goto err;
1418 
1419 	/*
1420 	 * If we were unable to get the V_WRITELOCK_TRUE, then we
1421 	 * may not have accurate after attrs, so check if
1422 	 * we have both attributes, they have a non-zero va_seq, and
1423 	 * va_seq has changed by exactly one,
1424 	 * if not, turn off the before attr.
1425 	 */
1426 	if (rwlock_ret != V_WRITELOCK_TRUE) {
1427 		if (bvap == NULL || avap == NULL ||
1428 		    bvap->va_seq == 0 || avap->va_seq == 0 ||
1429 		    avap->va_seq != (bvap->va_seq + 1)) {
1430 			bvap = NULL;
1431 		}
1432 	}
1433 
1434 	resp->status = NFS3_OK;
1435 	vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
1436 	resp->resok.count = args->count - uio.uio_resid;
1437 	resp->resok.committed = args->stable;
1438 	resp->resok.verf = write3verf;
1439 	goto out;
1440 
1441 err:
1442 	if (curthread->t_flag & T_WOULDBLOCK) {
1443 		curthread->t_flag &= ~T_WOULDBLOCK;
1444 		resp->status = NFS3ERR_JUKEBOX;
1445 	} else
1446 		resp->status = puterrno3(error);
1447 err1:
1448 	vattr_to_wcc_data(bvap, avap, &resp->resfail.file_wcc);
1449 out:
1450 	DTRACE_NFSV3_4(op__write__done, struct svc_req *, req,
1451 	    cred_t *, cr, vnode_t *, vp, WRITE3res *, resp);
1452 
1453 	if (vp != NULL) {
1454 		if (rwlock_ret != -1)
1455 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &ct);
1456 		if (in_crit)
1457 			nbl_end_crit(vp);
1458 		VN_RELE(vp);
1459 	}
1460 }
1461 
1462 void *
1463 rfs3_write_getfh(WRITE3args *args)
1464 {
1465 
1466 	return (&args->file);
1467 }
1468 
1469 void
1470 rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi,
1471 	struct svc_req *req, cred_t *cr)
1472 {
1473 	int error;
1474 	int in_crit = 0;
1475 	vnode_t *vp;
1476 	vnode_t *tvp = NULL;
1477 	vnode_t *dvp;
1478 	struct vattr *vap;
1479 	struct vattr va;
1480 	struct vattr *dbvap;
1481 	struct vattr dbva;
1482 	struct vattr *davap;
1483 	struct vattr dava;
1484 	enum vcexcl excl;
1485 	nfstime3 *mtime;
1486 	len_t reqsize;
1487 	bool_t trunc;
1488 
1489 	dbvap = NULL;
1490 	davap = NULL;
1491 
1492 	dvp = nfs3_fhtovp(&args->where.dir, exi);
1493 
1494 	DTRACE_NFSV3_4(op__create__start, struct svc_req *, req,
1495 	    cred_t *, cr, vnode_t *, dvp, CREATE3args *, args);
1496 
1497 	if (dvp == NULL) {
1498 		error = ESTALE;
1499 		goto out;
1500 	}
1501 
1502 #ifdef DEBUG
1503 	if (rfs3_do_pre_op_attr) {
1504 		dbva.va_mask = AT_ALL;
1505 		dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
1506 	} else
1507 		dbvap = NULL;
1508 #else
1509 	dbva.va_mask = AT_ALL;
1510 	dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
1511 #endif
1512 	davap = dbvap;
1513 
1514 	if (args->where.name == nfs3nametoolong) {
1515 		resp->status = NFS3ERR_NAMETOOLONG;
1516 		goto out1;
1517 	}
1518 
1519 	if (args->where.name == NULL || *(args->where.name) == '\0') {
1520 		resp->status = NFS3ERR_ACCES;
1521 		goto out1;
1522 	}
1523 
1524 	if (rdonly(exi, req)) {
1525 		resp->status = NFS3ERR_ROFS;
1526 		goto out1;
1527 	}
1528 
1529 	if (is_system_labeled()) {
1530 		bslabel_t *clabel = req->rq_label;
1531 
1532 		ASSERT(clabel != NULL);
1533 		DTRACE_PROBE2(tx__rfs3__log__info__opcreate__clabel, char *,
1534 		    "got client label from request(1)", struct svc_req *, req);
1535 
1536 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
1537 			if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK)) {
1538 				resp->status = NFS3ERR_ACCES;
1539 				goto out1;
1540 			}
1541 		}
1542 	}
1543 
1544 	if (args->how.mode == EXCLUSIVE) {
1545 		va.va_mask = AT_TYPE | AT_MODE | AT_MTIME;
1546 		va.va_type = VREG;
1547 		va.va_mode = (mode_t)0;
1548 		/*
1549 		 * Ensure no time overflows and that types match
1550 		 */
1551 		mtime = (nfstime3 *)&args->how.createhow3_u.verf;
1552 		va.va_mtime.tv_sec = mtime->seconds % INT32_MAX;
1553 		va.va_mtime.tv_nsec = mtime->nseconds;
1554 		excl = EXCL;
1555 	} else {
1556 		error = sattr3_to_vattr(&args->how.createhow3_u.obj_attributes,
1557 		    &va);
1558 		if (error)
1559 			goto out;
1560 		va.va_mask |= AT_TYPE;
1561 		va.va_type = VREG;
1562 		if (args->how.mode == GUARDED)
1563 			excl = EXCL;
1564 		else {
1565 			excl = NONEXCL;
1566 
1567 			/*
1568 			 * During creation of file in non-exclusive mode
1569 			 * if size of file is being set then make sure
1570 			 * that if the file already exists that no conflicting
1571 			 * non-blocking mandatory locks exists in the region
1572 			 * being modified. If there are conflicting locks fail
1573 			 * the operation with EACCES.
1574 			 */
1575 			if (va.va_mask & AT_SIZE) {
1576 				struct vattr tva;
1577 
1578 				/*
1579 				 * Does file already exist?
1580 				 */
1581 				error = VOP_LOOKUP(dvp, args->where.name, &tvp,
1582 				    NULL, 0, NULL, cr, NULL, NULL, NULL);
1583 
1584 				/*
1585 				 * Check to see if the file has been delegated
1586 				 * to a v4 client.  If so, then begin recall of
1587 				 * the delegation and return JUKEBOX to allow
1588 				 * the client to retrasmit its request.
1589 				 */
1590 
1591 				trunc = va.va_size == 0;
1592 				if (!error &&
1593 				    rfs4_check_delegated(FWRITE, tvp, trunc)) {
1594 					resp->status = NFS3ERR_JUKEBOX;
1595 					goto out1;
1596 				}
1597 
1598 				/*
1599 				 * Check for NBMAND lock conflicts
1600 				 */
1601 				if (!error && nbl_need_check(tvp)) {
1602 					u_offset_t offset;
1603 					ssize_t len;
1604 
1605 					nbl_start_crit(tvp, RW_READER);
1606 					in_crit = 1;
1607 
1608 					tva.va_mask = AT_SIZE;
1609 					error = VOP_GETATTR(tvp, &tva, 0, cr,
1610 					    NULL);
1611 					/*
1612 					 * Can't check for conflicts, so return
1613 					 * error.
1614 					 */
1615 					if (error)
1616 						goto out;
1617 
1618 					offset = tva.va_size < va.va_size ?
1619 					    tva.va_size : va.va_size;
1620 					len = tva.va_size < va.va_size ?
1621 					    va.va_size - tva.va_size :
1622 					    tva.va_size - va.va_size;
1623 					if (nbl_conflict(tvp, NBL_WRITE,
1624 					    offset, len, 0, NULL)) {
1625 						error = EACCES;
1626 						goto out;
1627 					}
1628 				} else if (tvp) {
1629 					VN_RELE(tvp);
1630 					tvp = NULL;
1631 				}
1632 			}
1633 		}
1634 		if (va.va_mask & AT_SIZE)
1635 			reqsize = va.va_size;
1636 	}
1637 
1638 	/*
1639 	 * Must specify the mode.
1640 	 */
1641 	if (!(va.va_mask & AT_MODE)) {
1642 		resp->status = NFS3ERR_INVAL;
1643 		goto out1;
1644 	}
1645 
1646 	/*
1647 	 * If the filesystem is exported with nosuid, then mask off
1648 	 * the setuid and setgid bits.
1649 	 */
1650 	if (va.va_type == VREG && (exi->exi_export.ex_flags & EX_NOSUID))
1651 		va.va_mode &= ~(VSUID | VSGID);
1652 
1653 tryagain:
1654 	/*
1655 	 * The file open mode used is VWRITE.  If the client needs
1656 	 * some other semantic, then it should do the access checking
1657 	 * itself.  It would have been nice to have the file open mode
1658 	 * passed as part of the arguments.
1659 	 */
1660 	error = VOP_CREATE(dvp, args->where.name, &va, excl, VWRITE,
1661 	    &vp, cr, 0, NULL, NULL);
1662 
1663 #ifdef DEBUG
1664 	if (rfs3_do_post_op_attr) {
1665 		dava.va_mask = AT_ALL;
1666 		davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
1667 	} else
1668 		davap = NULL;
1669 #else
1670 	dava.va_mask = AT_ALL;
1671 	davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
1672 #endif
1673 
1674 	if (error) {
1675 		/*
1676 		 * If we got something other than file already exists
1677 		 * then just return this error.  Otherwise, we got
1678 		 * EEXIST.  If we were doing a GUARDED create, then
1679 		 * just return this error.  Otherwise, we need to
1680 		 * make sure that this wasn't a duplicate of an
1681 		 * exclusive create request.
1682 		 *
1683 		 * The assumption is made that a non-exclusive create
1684 		 * request will never return EEXIST.
1685 		 */
1686 		if (error != EEXIST || args->how.mode == GUARDED)
1687 			goto out;
1688 		/*
1689 		 * Lookup the file so that we can get a vnode for it.
1690 		 */
1691 		error = VOP_LOOKUP(dvp, args->where.name, &vp, NULL, 0,
1692 		    NULL, cr, NULL, NULL, NULL);
1693 		if (error) {
1694 			/*
1695 			 * We couldn't find the file that we thought that
1696 			 * we just created.  So, we'll just try creating
1697 			 * it again.
1698 			 */
1699 			if (error == ENOENT)
1700 				goto tryagain;
1701 			goto out;
1702 		}
1703 
1704 		/*
1705 		 * If the file is delegated to a v4 client, go ahead
1706 		 * and initiate recall, this create is a hint that a
1707 		 * conflicting v3 open has occurred.
1708 		 */
1709 
1710 		if (rfs4_check_delegated(FWRITE, vp, FALSE)) {
1711 			VN_RELE(vp);
1712 			resp->status = NFS3ERR_JUKEBOX;
1713 			goto out1;
1714 		}
1715 
1716 		va.va_mask = AT_ALL;
1717 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1718 
1719 		mtime = (nfstime3 *)&args->how.createhow3_u.verf;
1720 		/* % with INT32_MAX to prevent overflows */
1721 		if (args->how.mode == EXCLUSIVE && (vap == NULL ||
1722 		    vap->va_mtime.tv_sec !=
1723 		    (mtime->seconds % INT32_MAX) ||
1724 		    vap->va_mtime.tv_nsec != mtime->nseconds)) {
1725 			VN_RELE(vp);
1726 			error = EEXIST;
1727 			goto out;
1728 		}
1729 	} else {
1730 
1731 		if ((args->how.mode == UNCHECKED ||
1732 		    args->how.mode == GUARDED) &&
1733 		    args->how.createhow3_u.obj_attributes.size.set_it &&
1734 		    va.va_size == 0)
1735 			trunc = TRUE;
1736 		else
1737 			trunc = FALSE;
1738 
1739 		if (rfs4_check_delegated(FWRITE, vp, trunc)) {
1740 			VN_RELE(vp);
1741 			resp->status = NFS3ERR_JUKEBOX;
1742 			goto out1;
1743 		}
1744 
1745 		va.va_mask = AT_ALL;
1746 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1747 
1748 		/*
1749 		 * We need to check to make sure that the file got
1750 		 * created to the indicated size.  If not, we do a
1751 		 * setattr to try to change the size, but we don't
1752 		 * try too hard.  This shouldn't a problem as most
1753 		 * clients will only specifiy a size of zero which
1754 		 * local file systems handle.  However, even if
1755 		 * the client does specify a non-zero size, it can
1756 		 * still recover by checking the size of the file
1757 		 * after it has created it and then issue a setattr
1758 		 * request of its own to set the size of the file.
1759 		 */
1760 		if (vap != NULL &&
1761 		    (args->how.mode == UNCHECKED ||
1762 		    args->how.mode == GUARDED) &&
1763 		    args->how.createhow3_u.obj_attributes.size.set_it &&
1764 		    vap->va_size != reqsize) {
1765 			va.va_mask = AT_SIZE;
1766 			va.va_size = reqsize;
1767 			(void) VOP_SETATTR(vp, &va, 0, cr, NULL);
1768 			va.va_mask = AT_ALL;
1769 			vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1770 		}
1771 	}
1772 
1773 #ifdef DEBUG
1774 	if (!rfs3_do_post_op_attr)
1775 		vap = NULL;
1776 #endif
1777 
1778 #ifdef DEBUG
1779 	if (!rfs3_do_post_op_fh3)
1780 		resp->resok.obj.handle_follows = FALSE;
1781 	else {
1782 #endif
1783 	error = makefh3(&resp->resok.obj.handle, vp, exi);
1784 	if (error)
1785 		resp->resok.obj.handle_follows = FALSE;
1786 	else
1787 		resp->resok.obj.handle_follows = TRUE;
1788 #ifdef DEBUG
1789 	}
1790 #endif
1791 
1792 	/*
1793 	 * Force modified data and metadata out to stable storage.
1794 	 */
1795 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
1796 	(void) VOP_FSYNC(dvp, 0, cr, NULL);
1797 
1798 	VN_RELE(vp);
1799 	if (tvp != NULL) {
1800 		if (in_crit)
1801 			nbl_end_crit(tvp);
1802 		VN_RELE(tvp);
1803 	}
1804 
1805 	resp->status = NFS3_OK;
1806 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
1807 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
1808 
1809 	DTRACE_NFSV3_4(op__create__done, struct svc_req *, req,
1810 	    cred_t *, cr, vnode_t *, dvp, CREATE3res *, resp);
1811 
1812 	VN_RELE(dvp);
1813 	return;
1814 
1815 out:
1816 	if (curthread->t_flag & T_WOULDBLOCK) {
1817 		curthread->t_flag &= ~T_WOULDBLOCK;
1818 		resp->status = NFS3ERR_JUKEBOX;
1819 	} else
1820 		resp->status = puterrno3(error);
1821 out1:
1822 	DTRACE_NFSV3_4(op__create__done, struct svc_req *, req,
1823 	    cred_t *, cr, vnode_t *, dvp, CREATE3res *, resp);
1824 
1825 	if (tvp != NULL) {
1826 		if (in_crit)
1827 			nbl_end_crit(tvp);
1828 		VN_RELE(tvp);
1829 	}
1830 	if (dvp != NULL)
1831 		VN_RELE(dvp);
1832 	vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
1833 }
1834 
1835 void *
1836 rfs3_create_getfh(CREATE3args *args)
1837 {
1838 
1839 	return (&args->where.dir);
1840 }
1841 
1842 void
1843 rfs3_mkdir(MKDIR3args *args, MKDIR3res *resp, struct exportinfo *exi,
1844 	struct svc_req *req, cred_t *cr)
1845 {
1846 	int error;
1847 	vnode_t *vp = NULL;
1848 	vnode_t *dvp;
1849 	struct vattr *vap;
1850 	struct vattr va;
1851 	struct vattr *dbvap;
1852 	struct vattr dbva;
1853 	struct vattr *davap;
1854 	struct vattr dava;
1855 
1856 	dbvap = NULL;
1857 	davap = NULL;
1858 
1859 	dvp = nfs3_fhtovp(&args->where.dir, exi);
1860 
1861 	DTRACE_NFSV3_4(op__mkdir__start, struct svc_req *, req,
1862 	    cred_t *, cr, vnode_t *, dvp, MKDIR3args *, args);
1863 
1864 	if (dvp == NULL) {
1865 		error = ESTALE;
1866 		goto out;
1867 	}
1868 
1869 #ifdef DEBUG
1870 	if (rfs3_do_pre_op_attr) {
1871 		dbva.va_mask = AT_ALL;
1872 		dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
1873 	} else
1874 		dbvap = NULL;
1875 #else
1876 	dbva.va_mask = AT_ALL;
1877 	dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
1878 #endif
1879 	davap = dbvap;
1880 
1881 	if (args->where.name == nfs3nametoolong) {
1882 		resp->status = NFS3ERR_NAMETOOLONG;
1883 		goto out1;
1884 	}
1885 
1886 	if (args->where.name == NULL || *(args->where.name) == '\0') {
1887 		resp->status = NFS3ERR_ACCES;
1888 		goto out1;
1889 	}
1890 
1891 	if (rdonly(exi, req)) {
1892 		resp->status = NFS3ERR_ROFS;
1893 		goto out1;
1894 	}
1895 
1896 	if (is_system_labeled()) {
1897 		bslabel_t *clabel = req->rq_label;
1898 
1899 		ASSERT(clabel != NULL);
1900 		DTRACE_PROBE2(tx__rfs3__log__info__opmkdir__clabel, char *,
1901 		    "got client label from request(1)", struct svc_req *, req);
1902 
1903 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
1904 			if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK)) {
1905 				resp->status = NFS3ERR_ACCES;
1906 				goto out1;
1907 			}
1908 		}
1909 	}
1910 
1911 	error = sattr3_to_vattr(&args->attributes, &va);
1912 	if (error)
1913 		goto out;
1914 
1915 	if (!(va.va_mask & AT_MODE)) {
1916 		resp->status = NFS3ERR_INVAL;
1917 		goto out1;
1918 	}
1919 
1920 	va.va_mask |= AT_TYPE;
1921 	va.va_type = VDIR;
1922 
1923 	error = VOP_MKDIR(dvp, args->where.name, &va, &vp, cr, NULL, 0, NULL);
1924 
1925 #ifdef DEBUG
1926 	if (rfs3_do_post_op_attr) {
1927 		dava.va_mask = AT_ALL;
1928 		davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
1929 	} else
1930 		davap = NULL;
1931 #else
1932 	dava.va_mask = AT_ALL;
1933 	davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
1934 #endif
1935 
1936 	/*
1937 	 * Force modified data and metadata out to stable storage.
1938 	 */
1939 	(void) VOP_FSYNC(dvp, 0, cr, NULL);
1940 
1941 	if (error)
1942 		goto out;
1943 
1944 #ifdef DEBUG
1945 	if (!rfs3_do_post_op_fh3)
1946 		resp->resok.obj.handle_follows = FALSE;
1947 	else {
1948 #endif
1949 	error = makefh3(&resp->resok.obj.handle, vp, exi);
1950 	if (error)
1951 		resp->resok.obj.handle_follows = FALSE;
1952 	else
1953 		resp->resok.obj.handle_follows = TRUE;
1954 #ifdef DEBUG
1955 	}
1956 #endif
1957 
1958 #ifdef DEBUG
1959 	if (rfs3_do_post_op_attr) {
1960 		va.va_mask = AT_ALL;
1961 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1962 	} else
1963 		vap = NULL;
1964 #else
1965 	va.va_mask = AT_ALL;
1966 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
1967 #endif
1968 
1969 	/*
1970 	 * Force modified data and metadata out to stable storage.
1971 	 */
1972 	(void) VOP_FSYNC(vp, 0, cr, NULL);
1973 
1974 	VN_RELE(vp);
1975 
1976 	resp->status = NFS3_OK;
1977 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
1978 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
1979 
1980 	DTRACE_NFSV3_4(op__mkdir__done, struct svc_req *, req,
1981 	    cred_t *, cr, vnode_t *, dvp, MKDIR3res *, resp);
1982 	VN_RELE(dvp);
1983 
1984 	return;
1985 
1986 out:
1987 	if (curthread->t_flag & T_WOULDBLOCK) {
1988 		curthread->t_flag &= ~T_WOULDBLOCK;
1989 		resp->status = NFS3ERR_JUKEBOX;
1990 	} else
1991 		resp->status = puterrno3(error);
1992 out1:
1993 	DTRACE_NFSV3_4(op__mkdir__done, struct svc_req *, req,
1994 	    cred_t *, cr, vnode_t *, dvp, MKDIR3res *, resp);
1995 	if (dvp != NULL)
1996 		VN_RELE(dvp);
1997 	vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
1998 }
1999 
2000 void *
2001 rfs3_mkdir_getfh(MKDIR3args *args)
2002 {
2003 
2004 	return (&args->where.dir);
2005 }
2006 
2007 void
2008 rfs3_symlink(SYMLINK3args *args, SYMLINK3res *resp, struct exportinfo *exi,
2009 	struct svc_req *req, cred_t *cr)
2010 {
2011 	int error;
2012 	vnode_t *vp;
2013 	vnode_t *dvp;
2014 	struct vattr *vap;
2015 	struct vattr va;
2016 	struct vattr *dbvap;
2017 	struct vattr dbva;
2018 	struct vattr *davap;
2019 	struct vattr dava;
2020 
2021 	dbvap = NULL;
2022 	davap = NULL;
2023 
2024 	dvp = nfs3_fhtovp(&args->where.dir, exi);
2025 
2026 	DTRACE_NFSV3_4(op__symlink__start, struct svc_req *, req,
2027 	    cred_t *, cr, vnode_t *, dvp, SYMLINK3args *, args);
2028 
2029 	if (dvp == NULL) {
2030 		error = ESTALE;
2031 		goto err;
2032 	}
2033 
2034 #ifdef DEBUG
2035 	if (rfs3_do_pre_op_attr) {
2036 		dbva.va_mask = AT_ALL;
2037 		dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
2038 	} else
2039 		dbvap = NULL;
2040 #else
2041 	dbva.va_mask = AT_ALL;
2042 	dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
2043 #endif
2044 	davap = dbvap;
2045 
2046 	if (args->where.name == nfs3nametoolong) {
2047 		resp->status = NFS3ERR_NAMETOOLONG;
2048 		goto err1;
2049 	}
2050 
2051 	if (args->where.name == NULL || *(args->where.name) == '\0') {
2052 		resp->status = NFS3ERR_ACCES;
2053 		goto err1;
2054 	}
2055 
2056 	if (rdonly(exi, req)) {
2057 		resp->status = NFS3ERR_ROFS;
2058 		goto err1;
2059 	}
2060 
2061 	if (is_system_labeled()) {
2062 		bslabel_t *clabel = req->rq_label;
2063 
2064 		ASSERT(clabel != NULL);
2065 		DTRACE_PROBE2(tx__rfs3__log__info__opsymlink__clabel, char *,
2066 		    "got client label from request(1)", struct svc_req *, req);
2067 
2068 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2069 			if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK)) {
2070 				resp->status = NFS3ERR_ACCES;
2071 				goto err1;
2072 			}
2073 		}
2074 	}
2075 
2076 	error = sattr3_to_vattr(&args->symlink.symlink_attributes, &va);
2077 	if (error)
2078 		goto err;
2079 
2080 	if (!(va.va_mask & AT_MODE)) {
2081 		resp->status = NFS3ERR_INVAL;
2082 		goto err1;
2083 	}
2084 
2085 	if (args->symlink.symlink_data == nfs3nametoolong) {
2086 		resp->status = NFS3ERR_NAMETOOLONG;
2087 		goto err1;
2088 	}
2089 
2090 	va.va_mask |= AT_TYPE;
2091 	va.va_type = VLNK;
2092 
2093 	error = VOP_SYMLINK(dvp, args->where.name, &va,
2094 	    args->symlink.symlink_data, cr, NULL, 0);
2095 
2096 #ifdef DEBUG
2097 	if (rfs3_do_post_op_attr) {
2098 		dava.va_mask = AT_ALL;
2099 		davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2100 	} else
2101 		davap = NULL;
2102 #else
2103 	dava.va_mask = AT_ALL;
2104 	davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2105 #endif
2106 
2107 	if (error)
2108 		goto err;
2109 
2110 	error = VOP_LOOKUP(dvp, args->where.name, &vp, NULL, 0, NULL, cr,
2111 	    NULL, NULL, NULL);
2112 
2113 	/*
2114 	 * Force modified data and metadata out to stable storage.
2115 	 */
2116 	(void) VOP_FSYNC(dvp, 0, cr, NULL);
2117 
2118 
2119 	resp->status = NFS3_OK;
2120 	if (error) {
2121 		resp->resok.obj.handle_follows = FALSE;
2122 		vattr_to_post_op_attr(NULL, &resp->resok.obj_attributes);
2123 		vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2124 		goto out;
2125 	}
2126 
2127 #ifdef DEBUG
2128 	if (!rfs3_do_post_op_fh3)
2129 		resp->resok.obj.handle_follows = FALSE;
2130 	else {
2131 #endif
2132 	error = makefh3(&resp->resok.obj.handle, vp, exi);
2133 	if (error)
2134 		resp->resok.obj.handle_follows = FALSE;
2135 	else
2136 		resp->resok.obj.handle_follows = TRUE;
2137 #ifdef DEBUG
2138 	}
2139 #endif
2140 
2141 #ifdef DEBUG
2142 	if (rfs3_do_post_op_attr) {
2143 		va.va_mask = AT_ALL;
2144 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2145 	} else
2146 		vap = NULL;
2147 #else
2148 	va.va_mask = AT_ALL;
2149 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2150 #endif
2151 
2152 	/*
2153 	 * Force modified data and metadata out to stable storage.
2154 	 */
2155 	(void) VOP_FSYNC(vp, 0, cr, NULL);
2156 
2157 	VN_RELE(vp);
2158 
2159 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
2160 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2161 	goto out;
2162 
2163 err:
2164 	if (curthread->t_flag & T_WOULDBLOCK) {
2165 		curthread->t_flag &= ~T_WOULDBLOCK;
2166 		resp->status = NFS3ERR_JUKEBOX;
2167 	} else
2168 		resp->status = puterrno3(error);
2169 err1:
2170 	vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
2171 out:
2172 	DTRACE_NFSV3_4(op__symlink__done, struct svc_req *, req,
2173 	    cred_t *, cr, vnode_t *, dvp, SYMLINK3res *, resp);
2174 
2175 	if (dvp != NULL)
2176 		VN_RELE(dvp);
2177 }
2178 
2179 void *
2180 rfs3_symlink_getfh(SYMLINK3args *args)
2181 {
2182 
2183 	return (&args->where.dir);
2184 }
2185 
2186 void
2187 rfs3_mknod(MKNOD3args *args, MKNOD3res *resp, struct exportinfo *exi,
2188 	struct svc_req *req, cred_t *cr)
2189 {
2190 	int error;
2191 	vnode_t *vp;
2192 	vnode_t *realvp;
2193 	vnode_t *dvp;
2194 	struct vattr *vap;
2195 	struct vattr va;
2196 	struct vattr *dbvap;
2197 	struct vattr dbva;
2198 	struct vattr *davap;
2199 	struct vattr dava;
2200 	int mode;
2201 	enum vcexcl excl;
2202 
2203 	dbvap = NULL;
2204 	davap = NULL;
2205 
2206 	dvp = nfs3_fhtovp(&args->where.dir, exi);
2207 
2208 	DTRACE_NFSV3_4(op__mknod__start, struct svc_req *, req,
2209 	    cred_t *, cr, vnode_t *, dvp, MKNOD3args *, args);
2210 
2211 	if (dvp == NULL) {
2212 		error = ESTALE;
2213 		goto out;
2214 	}
2215 
2216 #ifdef DEBUG
2217 	if (rfs3_do_pre_op_attr) {
2218 		dbva.va_mask = AT_ALL;
2219 		dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
2220 	} else
2221 		dbvap = NULL;
2222 #else
2223 	dbva.va_mask = AT_ALL;
2224 	dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva;
2225 #endif
2226 	davap = dbvap;
2227 
2228 	if (args->where.name == nfs3nametoolong) {
2229 		resp->status = NFS3ERR_NAMETOOLONG;
2230 		goto out1;
2231 	}
2232 
2233 	if (args->where.name == NULL || *(args->where.name) == '\0') {
2234 		resp->status = NFS3ERR_ACCES;
2235 		goto out1;
2236 	}
2237 
2238 	if (rdonly(exi, req)) {
2239 		resp->status = NFS3ERR_ROFS;
2240 		goto out1;
2241 	}
2242 
2243 	if (is_system_labeled()) {
2244 		bslabel_t *clabel = req->rq_label;
2245 
2246 		ASSERT(clabel != NULL);
2247 		DTRACE_PROBE2(tx__rfs3__log__info__opmknod__clabel, char *,
2248 		    "got client label from request(1)", struct svc_req *, req);
2249 
2250 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2251 			if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK)) {
2252 				resp->status = NFS3ERR_ACCES;
2253 				goto out1;
2254 			}
2255 		}
2256 	}
2257 
2258 	switch (args->what.type) {
2259 	case NF3CHR:
2260 	case NF3BLK:
2261 		error = sattr3_to_vattr(
2262 		    &args->what.mknoddata3_u.device.dev_attributes, &va);
2263 		if (error)
2264 			goto out;
2265 		if (secpolicy_sys_devices(cr) != 0) {
2266 			resp->status = NFS3ERR_PERM;
2267 			goto out1;
2268 		}
2269 		if (args->what.type == NF3CHR)
2270 			va.va_type = VCHR;
2271 		else
2272 			va.va_type = VBLK;
2273 		va.va_rdev = makedevice(
2274 		    args->what.mknoddata3_u.device.spec.specdata1,
2275 		    args->what.mknoddata3_u.device.spec.specdata2);
2276 		va.va_mask |= AT_TYPE | AT_RDEV;
2277 		break;
2278 	case NF3SOCK:
2279 		error = sattr3_to_vattr(
2280 		    &args->what.mknoddata3_u.pipe_attributes, &va);
2281 		if (error)
2282 			goto out;
2283 		va.va_type = VSOCK;
2284 		va.va_mask |= AT_TYPE;
2285 		break;
2286 	case NF3FIFO:
2287 		error = sattr3_to_vattr(
2288 		    &args->what.mknoddata3_u.pipe_attributes, &va);
2289 		if (error)
2290 			goto out;
2291 		va.va_type = VFIFO;
2292 		va.va_mask |= AT_TYPE;
2293 		break;
2294 	default:
2295 		resp->status = NFS3ERR_BADTYPE;
2296 		goto out1;
2297 	}
2298 
2299 	/*
2300 	 * Must specify the mode.
2301 	 */
2302 	if (!(va.va_mask & AT_MODE)) {
2303 		resp->status = NFS3ERR_INVAL;
2304 		goto out1;
2305 	}
2306 
2307 	excl = EXCL;
2308 
2309 	mode = 0;
2310 
2311 	error = VOP_CREATE(dvp, args->where.name, &va, excl, mode,
2312 	    &vp, cr, 0, NULL, NULL);
2313 
2314 #ifdef DEBUG
2315 	if (rfs3_do_post_op_attr) {
2316 		dava.va_mask = AT_ALL;
2317 		davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2318 	} else
2319 		davap = NULL;
2320 #else
2321 	dava.va_mask = AT_ALL;
2322 	davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava;
2323 #endif
2324 
2325 	/*
2326 	 * Force modified data and metadata out to stable storage.
2327 	 */
2328 	(void) VOP_FSYNC(dvp, 0, cr, NULL);
2329 
2330 	if (error)
2331 		goto out;
2332 
2333 	resp->status = NFS3_OK;
2334 
2335 #ifdef DEBUG
2336 	if (!rfs3_do_post_op_fh3)
2337 		resp->resok.obj.handle_follows = FALSE;
2338 	else {
2339 #endif
2340 	error = makefh3(&resp->resok.obj.handle, vp, exi);
2341 	if (error)
2342 		resp->resok.obj.handle_follows = FALSE;
2343 	else
2344 		resp->resok.obj.handle_follows = TRUE;
2345 #ifdef DEBUG
2346 	}
2347 #endif
2348 
2349 #ifdef DEBUG
2350 	if (rfs3_do_post_op_attr) {
2351 		va.va_mask = AT_ALL;
2352 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2353 	} else
2354 		vap = NULL;
2355 #else
2356 	va.va_mask = AT_ALL;
2357 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2358 #endif
2359 
2360 	/*
2361 	 * Force modified metadata out to stable storage.
2362 	 *
2363 	 * if a underlying vp exists, pass it to VOP_FSYNC
2364 	 */
2365 	if (VOP_REALVP(vp, &realvp, NULL) == 0)
2366 		(void) VOP_FSYNC(realvp, FNODSYNC, cr, NULL);
2367 	else
2368 		(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
2369 
2370 	VN_RELE(vp);
2371 
2372 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
2373 	vattr_to_wcc_data(dbvap, davap, &resp->resok.dir_wcc);
2374 	DTRACE_NFSV3_4(op__mknod__done, struct svc_req *, req,
2375 	    cred_t *, cr, vnode_t *, dvp, MKNOD3res *, resp);
2376 	VN_RELE(dvp);
2377 	return;
2378 
2379 out:
2380 	if (curthread->t_flag & T_WOULDBLOCK) {
2381 		curthread->t_flag &= ~T_WOULDBLOCK;
2382 		resp->status = NFS3ERR_JUKEBOX;
2383 	} else
2384 		resp->status = puterrno3(error);
2385 out1:
2386 	DTRACE_NFSV3_4(op__mknod__done, struct svc_req *, req,
2387 	    cred_t *, cr, vnode_t *, dvp, MKNOD3res *, resp);
2388 	if (dvp != NULL)
2389 		VN_RELE(dvp);
2390 	vattr_to_wcc_data(dbvap, davap, &resp->resfail.dir_wcc);
2391 }
2392 
2393 void *
2394 rfs3_mknod_getfh(MKNOD3args *args)
2395 {
2396 
2397 	return (&args->where.dir);
2398 }
2399 
2400 void
2401 rfs3_remove(REMOVE3args *args, REMOVE3res *resp, struct exportinfo *exi,
2402 	struct svc_req *req, cred_t *cr)
2403 {
2404 	int error = 0;
2405 	vnode_t *vp;
2406 	struct vattr *bvap;
2407 	struct vattr bva;
2408 	struct vattr *avap;
2409 	struct vattr ava;
2410 	vnode_t *targvp = NULL;
2411 
2412 	bvap = NULL;
2413 	avap = NULL;
2414 
2415 	vp = nfs3_fhtovp(&args->object.dir, exi);
2416 
2417 	DTRACE_NFSV3_4(op__remove__start, struct svc_req *, req,
2418 	    cred_t *, cr, vnode_t *, vp, REMOVE3args *, args);
2419 
2420 	if (vp == NULL) {
2421 		error = ESTALE;
2422 		goto err;
2423 	}
2424 
2425 #ifdef DEBUG
2426 	if (rfs3_do_pre_op_attr) {
2427 		bva.va_mask = AT_ALL;
2428 		bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva;
2429 	} else
2430 		bvap = NULL;
2431 #else
2432 	bva.va_mask = AT_ALL;
2433 	bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva;
2434 #endif
2435 	avap = bvap;
2436 
2437 	if (vp->v_type != VDIR) {
2438 		resp->status = NFS3ERR_NOTDIR;
2439 		goto err1;
2440 	}
2441 
2442 	if (args->object.name == nfs3nametoolong) {
2443 		resp->status = NFS3ERR_NAMETOOLONG;
2444 		goto err1;
2445 	}
2446 
2447 	if (args->object.name == NULL || *(args->object.name) == '\0') {
2448 		resp->status = NFS3ERR_ACCES;
2449 		goto err1;
2450 	}
2451 
2452 	if (rdonly(exi, req)) {
2453 		resp->status = NFS3ERR_ROFS;
2454 		goto err1;
2455 	}
2456 
2457 	if (is_system_labeled()) {
2458 		bslabel_t *clabel = req->rq_label;
2459 
2460 		ASSERT(clabel != NULL);
2461 		DTRACE_PROBE2(tx__rfs3__log__info__opremove__clabel, char *,
2462 		    "got client label from request(1)", struct svc_req *, req);
2463 
2464 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2465 			if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK)) {
2466 				resp->status = NFS3ERR_ACCES;
2467 				goto err1;
2468 			}
2469 		}
2470 	}
2471 
2472 	/*
2473 	 * Check for a conflict with a non-blocking mandatory share
2474 	 * reservation and V4 delegations
2475 	 */
2476 	error = VOP_LOOKUP(vp, args->object.name, &targvp, NULL, 0,
2477 	    NULL, cr, NULL, NULL, NULL);
2478 	if (error != 0)
2479 		goto err;
2480 
2481 	if (rfs4_check_delegated(FWRITE, targvp, TRUE)) {
2482 		resp->status = NFS3ERR_JUKEBOX;
2483 		goto err1;
2484 	}
2485 
2486 	if (!nbl_need_check(targvp)) {
2487 		error = VOP_REMOVE(vp, args->object.name, cr, NULL, 0);
2488 	} else {
2489 		nbl_start_crit(targvp, RW_READER);
2490 		if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) {
2491 			error = EACCES;
2492 		} else {
2493 			error = VOP_REMOVE(vp, args->object.name, cr, NULL, 0);
2494 		}
2495 		nbl_end_crit(targvp);
2496 	}
2497 	VN_RELE(targvp);
2498 	targvp = NULL;
2499 
2500 #ifdef DEBUG
2501 	if (rfs3_do_post_op_attr) {
2502 		ava.va_mask = AT_ALL;
2503 		avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
2504 	} else
2505 		avap = NULL;
2506 #else
2507 	ava.va_mask = AT_ALL;
2508 	avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
2509 #endif
2510 
2511 	/*
2512 	 * Force modified data and metadata out to stable storage.
2513 	 */
2514 	(void) VOP_FSYNC(vp, 0, cr, NULL);
2515 
2516 	if (error)
2517 		goto err;
2518 
2519 	resp->status = NFS3_OK;
2520 	vattr_to_wcc_data(bvap, avap, &resp->resok.dir_wcc);
2521 	goto out;
2522 
2523 err:
2524 	if (curthread->t_flag & T_WOULDBLOCK) {
2525 		curthread->t_flag &= ~T_WOULDBLOCK;
2526 		resp->status = NFS3ERR_JUKEBOX;
2527 	} else
2528 		resp->status = puterrno3(error);
2529 err1:
2530 	vattr_to_wcc_data(bvap, avap, &resp->resfail.dir_wcc);
2531 out:
2532 	DTRACE_NFSV3_4(op__remove__done, struct svc_req *, req,
2533 	    cred_t *, cr, vnode_t *, vp, REMOVE3res *, resp);
2534 	if (vp != NULL)
2535 		VN_RELE(vp);
2536 }
2537 
2538 void *
2539 rfs3_remove_getfh(REMOVE3args *args)
2540 {
2541 
2542 	return (&args->object.dir);
2543 }
2544 
2545 void
2546 rfs3_rmdir(RMDIR3args *args, RMDIR3res *resp, struct exportinfo *exi,
2547 	struct svc_req *req, cred_t *cr)
2548 {
2549 	int error;
2550 	vnode_t *vp;
2551 	struct vattr *bvap;
2552 	struct vattr bva;
2553 	struct vattr *avap;
2554 	struct vattr ava;
2555 
2556 	bvap = NULL;
2557 	avap = NULL;
2558 
2559 	vp = nfs3_fhtovp(&args->object.dir, exi);
2560 
2561 	DTRACE_NFSV3_4(op__rmdir__start, struct svc_req *, req,
2562 	    cred_t *, cr, vnode_t *, vp, RMDIR3args *, args);
2563 
2564 	if (vp == NULL) {
2565 		error = ESTALE;
2566 		goto err;
2567 	}
2568 
2569 #ifdef DEBUG
2570 	if (rfs3_do_pre_op_attr) {
2571 		bva.va_mask = AT_ALL;
2572 		bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva;
2573 	} else
2574 		bvap = NULL;
2575 #else
2576 	bva.va_mask = AT_ALL;
2577 	bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva;
2578 #endif
2579 	avap = bvap;
2580 
2581 	if (vp->v_type != VDIR) {
2582 		resp->status = NFS3ERR_NOTDIR;
2583 		goto err1;
2584 	}
2585 
2586 	if (args->object.name == nfs3nametoolong) {
2587 		resp->status = NFS3ERR_NAMETOOLONG;
2588 		goto err1;
2589 	}
2590 
2591 	if (args->object.name == NULL || *(args->object.name) == '\0') {
2592 		resp->status = NFS3ERR_ACCES;
2593 		goto err1;
2594 	}
2595 
2596 	if (rdonly(exi, req)) {
2597 		resp->status = NFS3ERR_ROFS;
2598 		goto err1;
2599 	}
2600 
2601 	if (is_system_labeled()) {
2602 		bslabel_t *clabel = req->rq_label;
2603 
2604 		ASSERT(clabel != NULL);
2605 		DTRACE_PROBE2(tx__rfs3__log__info__opremovedir__clabel, char *,
2606 		    "got client label from request(1)", struct svc_req *, req);
2607 
2608 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2609 			if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK)) {
2610 				resp->status = NFS3ERR_ACCES;
2611 				goto err1;
2612 			}
2613 		}
2614 	}
2615 
2616 	error = VOP_RMDIR(vp, args->object.name, rootdir, cr, NULL, 0);
2617 
2618 #ifdef DEBUG
2619 	if (rfs3_do_post_op_attr) {
2620 		ava.va_mask = AT_ALL;
2621 		avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
2622 	} else
2623 		avap = NULL;
2624 #else
2625 	ava.va_mask = AT_ALL;
2626 	avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
2627 #endif
2628 
2629 	/*
2630 	 * Force modified data and metadata out to stable storage.
2631 	 */
2632 	(void) VOP_FSYNC(vp, 0, cr, NULL);
2633 
2634 	if (error) {
2635 		/*
2636 		 * System V defines rmdir to return EEXIST, not ENOTEMPTY,
2637 		 * if the directory is not empty.  A System V NFS server
2638 		 * needs to map NFS3ERR_EXIST to NFS3ERR_NOTEMPTY to transmit
2639 		 * over the wire.
2640 		 */
2641 		if (error == EEXIST)
2642 			error = ENOTEMPTY;
2643 		goto err;
2644 	}
2645 
2646 	resp->status = NFS3_OK;
2647 	vattr_to_wcc_data(bvap, avap, &resp->resok.dir_wcc);
2648 	goto out;
2649 
2650 err:
2651 	if (curthread->t_flag & T_WOULDBLOCK) {
2652 		curthread->t_flag &= ~T_WOULDBLOCK;
2653 		resp->status = NFS3ERR_JUKEBOX;
2654 	} else
2655 		resp->status = puterrno3(error);
2656 err1:
2657 	vattr_to_wcc_data(bvap, avap, &resp->resfail.dir_wcc);
2658 out:
2659 	DTRACE_NFSV3_4(op__rmdir__done, struct svc_req *, req,
2660 	    cred_t *, cr, vnode_t *, vp, RMDIR3res *, resp);
2661 	if (vp != NULL)
2662 		VN_RELE(vp);
2663 
2664 }
2665 
2666 void *
2667 rfs3_rmdir_getfh(RMDIR3args *args)
2668 {
2669 
2670 	return (&args->object.dir);
2671 }
2672 
2673 void
2674 rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi,
2675 	struct svc_req *req, cred_t *cr)
2676 {
2677 	int error = 0;
2678 	vnode_t *fvp;
2679 	vnode_t *tvp;
2680 	vnode_t *targvp;
2681 	struct vattr *fbvap;
2682 	struct vattr fbva;
2683 	struct vattr *favap;
2684 	struct vattr fava;
2685 	struct vattr *tbvap;
2686 	struct vattr tbva;
2687 	struct vattr *tavap;
2688 	struct vattr tava;
2689 	nfs_fh3 *fh3;
2690 	struct exportinfo *to_exi;
2691 	vnode_t *srcvp = NULL;
2692 	bslabel_t *clabel;
2693 
2694 	fbvap = NULL;
2695 	favap = NULL;
2696 	tbvap = NULL;
2697 	tavap = NULL;
2698 	tvp = NULL;
2699 
2700 	fvp = nfs3_fhtovp(&args->from.dir, exi);
2701 
2702 	DTRACE_NFSV3_4(op__rename__start, struct svc_req *, req,
2703 	    cred_t *, cr, vnode_t *, fvp, RENAME3args *, args);
2704 
2705 	if (fvp == NULL) {
2706 		error = ESTALE;
2707 		goto err;
2708 	}
2709 
2710 	if (is_system_labeled()) {
2711 		clabel = req->rq_label;
2712 		ASSERT(clabel != NULL);
2713 		DTRACE_PROBE2(tx__rfs3__log__info__oprename__clabel, char *,
2714 		    "got client label from request(1)", struct svc_req *, req);
2715 
2716 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2717 			if (!do_rfs_label_check(clabel, fvp, EQUALITY_CHECK)) {
2718 				resp->status = NFS3ERR_ACCES;
2719 				goto err1;
2720 			}
2721 		}
2722 	}
2723 
2724 #ifdef DEBUG
2725 	if (rfs3_do_pre_op_attr) {
2726 		fbva.va_mask = AT_ALL;
2727 		fbvap = VOP_GETATTR(fvp, &fbva, 0, cr, NULL) ? NULL : &fbva;
2728 	} else
2729 		fbvap = NULL;
2730 #else
2731 	fbva.va_mask = AT_ALL;
2732 	fbvap = VOP_GETATTR(fvp, &fbva, 0, cr, NULL) ? NULL : &fbva;
2733 #endif
2734 	favap = fbvap;
2735 
2736 	fh3 = &args->to.dir;
2737 	to_exi = checkexport(&fh3->fh3_fsid, FH3TOXFIDP(fh3));
2738 	if (to_exi == NULL) {
2739 		resp->status = NFS3ERR_ACCES;
2740 		goto err1;
2741 	}
2742 	exi_rele(to_exi);
2743 
2744 	if (to_exi != exi) {
2745 		resp->status = NFS3ERR_XDEV;
2746 		goto err1;
2747 	}
2748 
2749 	tvp = nfs3_fhtovp(&args->to.dir, exi);
2750 	if (tvp == NULL) {
2751 		error = ESTALE;
2752 		goto err;
2753 	}
2754 
2755 #ifdef DEBUG
2756 	if (rfs3_do_pre_op_attr) {
2757 		tbva.va_mask = AT_ALL;
2758 		tbvap = VOP_GETATTR(tvp, &tbva, 0, cr, NULL) ? NULL : &tbva;
2759 	} else
2760 		tbvap = NULL;
2761 #else
2762 	tbva.va_mask = AT_ALL;
2763 	tbvap = VOP_GETATTR(tvp, &tbva, 0, cr, NULL) ? NULL : &tbva;
2764 #endif
2765 	tavap = tbvap;
2766 
2767 	if (fvp->v_type != VDIR || tvp->v_type != VDIR) {
2768 		resp->status = NFS3ERR_NOTDIR;
2769 		goto err1;
2770 	}
2771 
2772 	if (args->from.name == nfs3nametoolong ||
2773 	    args->to.name == nfs3nametoolong) {
2774 		resp->status = NFS3ERR_NAMETOOLONG;
2775 		goto err1;
2776 	}
2777 	if (args->from.name == NULL || *(args->from.name) == '\0' ||
2778 	    args->to.name == NULL || *(args->to.name) == '\0') {
2779 		resp->status = NFS3ERR_ACCES;
2780 		goto err1;
2781 	}
2782 
2783 	if (rdonly(exi, req)) {
2784 		resp->status = NFS3ERR_ROFS;
2785 		goto err1;
2786 	}
2787 
2788 	if (is_system_labeled()) {
2789 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2790 			if (!do_rfs_label_check(clabel, tvp, EQUALITY_CHECK)) {
2791 				resp->status = NFS3ERR_ACCES;
2792 				goto err1;
2793 			}
2794 		}
2795 	}
2796 
2797 	/*
2798 	 * Check for a conflict with a non-blocking mandatory share
2799 	 * reservation or V4 delegations.
2800 	 */
2801 	error = VOP_LOOKUP(fvp, args->from.name, &srcvp, NULL, 0,
2802 	    NULL, cr, NULL, NULL, NULL);
2803 	if (error != 0)
2804 		goto err;
2805 
2806 	/*
2807 	 * If we rename a delegated file we should recall the
2808 	 * delegation, since future opens should fail or would
2809 	 * refer to a new file.
2810 	 */
2811 	if (rfs4_check_delegated(FWRITE, srcvp, FALSE)) {
2812 		resp->status = NFS3ERR_JUKEBOX;
2813 		goto err1;
2814 	}
2815 
2816 	/*
2817 	 * Check for renaming over a delegated file.  Check rfs4_deleg_policy
2818 	 * first to avoid VOP_LOOKUP if possible.
2819 	 */
2820 	if (rfs4_deleg_policy != SRV_NEVER_DELEGATE &&
2821 	    VOP_LOOKUP(tvp, args->to.name, &targvp, NULL, 0, NULL, cr,
2822 	    NULL, NULL, NULL) == 0) {
2823 
2824 		if (rfs4_check_delegated(FWRITE, targvp, TRUE)) {
2825 			VN_RELE(targvp);
2826 			resp->status = NFS3ERR_JUKEBOX;
2827 			goto err1;
2828 		}
2829 		VN_RELE(targvp);
2830 	}
2831 
2832 	if (!nbl_need_check(srcvp)) {
2833 		error = VOP_RENAME(fvp, args->from.name, tvp,
2834 		    args->to.name, cr, NULL, 0);
2835 	} else {
2836 		nbl_start_crit(srcvp, RW_READER);
2837 		if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0, NULL)) {
2838 			error = EACCES;
2839 		} else {
2840 			error = VOP_RENAME(fvp, args->from.name, tvp,
2841 			    args->to.name, cr, NULL, 0);
2842 		}
2843 		nbl_end_crit(srcvp);
2844 	}
2845 	if (error == 0)
2846 		vn_renamepath(tvp, srcvp, args->to.name,
2847 		    strlen(args->to.name));
2848 	VN_RELE(srcvp);
2849 	srcvp = NULL;
2850 
2851 #ifdef DEBUG
2852 	if (rfs3_do_post_op_attr) {
2853 		fava.va_mask = AT_ALL;
2854 		favap = VOP_GETATTR(fvp, &fava, 0, cr, NULL) ? NULL : &fava;
2855 		tava.va_mask = AT_ALL;
2856 		tavap = VOP_GETATTR(tvp, &tava, 0, cr, NULL) ? NULL : &tava;
2857 	} else {
2858 		favap = NULL;
2859 		tavap = NULL;
2860 	}
2861 #else
2862 	fava.va_mask = AT_ALL;
2863 	favap = VOP_GETATTR(fvp, &fava, 0, cr, NULL) ? NULL : &fava;
2864 	tava.va_mask = AT_ALL;
2865 	tavap = VOP_GETATTR(tvp, &tava, 0, cr, NULL) ? NULL : &tava;
2866 #endif
2867 
2868 	/*
2869 	 * Force modified data and metadata out to stable storage.
2870 	 */
2871 	(void) VOP_FSYNC(fvp, 0, cr, NULL);
2872 	(void) VOP_FSYNC(tvp, 0, cr, NULL);
2873 
2874 	if (error)
2875 		goto err;
2876 
2877 	resp->status = NFS3_OK;
2878 	vattr_to_wcc_data(fbvap, favap, &resp->resok.fromdir_wcc);
2879 	vattr_to_wcc_data(tbvap, tavap, &resp->resok.todir_wcc);
2880 	goto out;
2881 
2882 err:
2883 	if (curthread->t_flag & T_WOULDBLOCK) {
2884 		curthread->t_flag &= ~T_WOULDBLOCK;
2885 		resp->status = NFS3ERR_JUKEBOX;
2886 	} else
2887 		resp->status = puterrno3(error);
2888 err1:
2889 	vattr_to_wcc_data(fbvap, favap, &resp->resfail.fromdir_wcc);
2890 	vattr_to_wcc_data(tbvap, tavap, &resp->resfail.todir_wcc);
2891 out:
2892 	DTRACE_NFSV3_4(op__rename__done, struct svc_req *, req,
2893 	    cred_t *, cr, vnode_t *, fvp, RENAME3res *, resp);
2894 	if (fvp != NULL)
2895 		VN_RELE(fvp);
2896 	if (tvp != NULL)
2897 		VN_RELE(tvp);
2898 }
2899 
2900 void *
2901 rfs3_rename_getfh(RENAME3args *args)
2902 {
2903 
2904 	return (&args->from.dir);
2905 }
2906 
2907 void
2908 rfs3_link(LINK3args *args, LINK3res *resp, struct exportinfo *exi,
2909 	struct svc_req *req, cred_t *cr)
2910 {
2911 	int error;
2912 	vnode_t *vp;
2913 	vnode_t *dvp;
2914 	struct vattr *vap;
2915 	struct vattr va;
2916 	struct vattr *bvap;
2917 	struct vattr bva;
2918 	struct vattr *avap;
2919 	struct vattr ava;
2920 	nfs_fh3	*fh3;
2921 	struct exportinfo *to_exi;
2922 	bslabel_t *clabel;
2923 
2924 	vap = NULL;
2925 	bvap = NULL;
2926 	avap = NULL;
2927 	dvp = NULL;
2928 
2929 	vp = nfs3_fhtovp(&args->file, exi);
2930 
2931 	DTRACE_NFSV3_4(op__link__start, struct svc_req *, req,
2932 	    cred_t *, cr, vnode_t *, vp, LINK3args *, args);
2933 
2934 	if (vp == NULL) {
2935 		error = ESTALE;
2936 		goto out;
2937 	}
2938 
2939 #ifdef DEBUG
2940 	if (rfs3_do_pre_op_attr) {
2941 		va.va_mask = AT_ALL;
2942 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2943 	} else
2944 		vap = NULL;
2945 #else
2946 	va.va_mask = AT_ALL;
2947 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
2948 #endif
2949 
2950 	fh3 = &args->link.dir;
2951 	to_exi = checkexport(&fh3->fh3_fsid, FH3TOXFIDP(fh3));
2952 	if (to_exi == NULL) {
2953 		resp->status = NFS3ERR_ACCES;
2954 		goto out1;
2955 	}
2956 	exi_rele(to_exi);
2957 
2958 	if (to_exi != exi) {
2959 		resp->status = NFS3ERR_XDEV;
2960 		goto out1;
2961 	}
2962 
2963 	if (is_system_labeled()) {
2964 		clabel = req->rq_label;
2965 
2966 		ASSERT(clabel != NULL);
2967 		DTRACE_PROBE2(tx__rfs3__log__info__oplink__clabel, char *,
2968 		    "got client label from request(1)", struct svc_req *, req);
2969 
2970 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
2971 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
2972 				resp->status = NFS3ERR_ACCES;
2973 				goto out1;
2974 			}
2975 		}
2976 	}
2977 
2978 	dvp = nfs3_fhtovp(&args->link.dir, exi);
2979 	if (dvp == NULL) {
2980 		error = ESTALE;
2981 		goto out;
2982 	}
2983 
2984 #ifdef DEBUG
2985 	if (rfs3_do_pre_op_attr) {
2986 		bva.va_mask = AT_ALL;
2987 		bvap = VOP_GETATTR(dvp, &bva, 0, cr, NULL) ? NULL : &bva;
2988 	} else
2989 		bvap = NULL;
2990 #else
2991 	bva.va_mask = AT_ALL;
2992 	bvap = VOP_GETATTR(dvp, &bva, 0, cr, NULL) ? NULL : &bva;
2993 #endif
2994 
2995 	if (dvp->v_type != VDIR) {
2996 		resp->status = NFS3ERR_NOTDIR;
2997 		goto out1;
2998 	}
2999 
3000 	if (args->link.name == nfs3nametoolong) {
3001 		resp->status = NFS3ERR_NAMETOOLONG;
3002 		goto out1;
3003 	}
3004 
3005 	if (args->link.name == NULL || *(args->link.name) == '\0') {
3006 		resp->status = NFS3ERR_ACCES;
3007 		goto out1;
3008 	}
3009 
3010 	if (rdonly(exi, req)) {
3011 		resp->status = NFS3ERR_ROFS;
3012 		goto out1;
3013 	}
3014 
3015 	if (is_system_labeled()) {
3016 		DTRACE_PROBE2(tx__rfs3__log__info__oplinkdir__clabel, char *,
3017 		    "got client label from request(1)", struct svc_req *, req);
3018 
3019 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
3020 			if (!do_rfs_label_check(clabel, dvp, EQUALITY_CHECK)) {
3021 				resp->status = NFS3ERR_ACCES;
3022 				goto out1;
3023 			}
3024 		}
3025 	}
3026 
3027 	error = VOP_LINK(dvp, vp, args->link.name, cr, NULL, 0);
3028 
3029 #ifdef DEBUG
3030 	if (rfs3_do_post_op_attr) {
3031 		va.va_mask = AT_ALL;
3032 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3033 		ava.va_mask = AT_ALL;
3034 		avap = VOP_GETATTR(dvp, &ava, 0, cr, NULL) ? NULL : &ava;
3035 	} else {
3036 		vap = NULL;
3037 		avap = NULL;
3038 	}
3039 #else
3040 	va.va_mask = AT_ALL;
3041 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3042 	ava.va_mask = AT_ALL;
3043 	avap = VOP_GETATTR(dvp, &ava, 0, cr, NULL) ? NULL : &ava;
3044 #endif
3045 
3046 	/*
3047 	 * Force modified data and metadata out to stable storage.
3048 	 */
3049 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
3050 	(void) VOP_FSYNC(dvp, 0, cr, NULL);
3051 
3052 	if (error)
3053 		goto out;
3054 
3055 	VN_RELE(dvp);
3056 
3057 	resp->status = NFS3_OK;
3058 	vattr_to_post_op_attr(vap, &resp->resok.file_attributes);
3059 	vattr_to_wcc_data(bvap, avap, &resp->resok.linkdir_wcc);
3060 
3061 	DTRACE_NFSV3_4(op__link__done, struct svc_req *, req,
3062 	    cred_t *, cr, vnode_t *, vp, LINK3res *, resp);
3063 
3064 	VN_RELE(vp);
3065 
3066 	return;
3067 
3068 out:
3069 	if (curthread->t_flag & T_WOULDBLOCK) {
3070 		curthread->t_flag &= ~T_WOULDBLOCK;
3071 		resp->status = NFS3ERR_JUKEBOX;
3072 	} else
3073 		resp->status = puterrno3(error);
3074 out1:
3075 	DTRACE_NFSV3_4(op__link__done, struct svc_req *, req,
3076 	    cred_t *, cr, vnode_t *, vp, LINK3res *, resp);
3077 
3078 	if (vp != NULL)
3079 		VN_RELE(vp);
3080 	if (dvp != NULL)
3081 		VN_RELE(dvp);
3082 	vattr_to_post_op_attr(vap, &resp->resfail.file_attributes);
3083 	vattr_to_wcc_data(bvap, avap, &resp->resfail.linkdir_wcc);
3084 }
3085 
3086 void *
3087 rfs3_link_getfh(LINK3args *args)
3088 {
3089 
3090 	return (&args->file);
3091 }
3092 
3093 /*
3094  * This macro defines the size of a response which contains attribute
3095  * information and one directory entry (whose length is specified by
3096  * the macro parameter).  If the incoming request is larger than this,
3097  * then we are guaranteed to be able to return at one directory entry
3098  * if one exists.  Therefore, we do not need to check for
3099  * NFS3ERR_TOOSMALL if the requested size is larger then this.  If it
3100  * is not, then we need to check to make sure that this error does not
3101  * need to be returned.
3102  *
3103  * NFS3_READDIR_MIN_COUNT is comprised of following :
3104  *
3105  * status - 1 * BYTES_PER_XDR_UNIT
3106  * attr. flag - 1 * BYTES_PER_XDR_UNIT
3107  * cookie verifier - 2 * BYTES_PER_XDR_UNIT
3108  * attributes  - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT
3109  * boolean - 1 * BYTES_PER_XDR_UNIT
3110  * file id - 2 * BYTES_PER_XDR_UNIT
3111  * directory name length - 1 * BYTES_PER_XDR_UNIT
3112  * cookie - 2 * BYTES_PER_XDR_UNIT
3113  * end of list - 1 * BYTES_PER_XDR_UNIT
3114  * end of file - 1 * BYTES_PER_XDR_UNIT
3115  * Name length of directory to the nearest byte
3116  */
3117 
3118 #define	NFS3_READDIR_MIN_COUNT(length)	\
3119 	((1 + 1 + 2 + NFS3_SIZEOF_FATTR3 + 1 + 2 + 1 + 2 + 1 + 1) * \
3120 		BYTES_PER_XDR_UNIT + roundup((length), BYTES_PER_XDR_UNIT))
3121 
3122 /* ARGSUSED */
3123 void
3124 rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi,
3125 	struct svc_req *req, cred_t *cr)
3126 {
3127 	int error;
3128 	vnode_t *vp;
3129 	struct vattr *vap;
3130 	struct vattr va;
3131 	struct iovec iov;
3132 	struct uio uio;
3133 	char *data;
3134 	int iseof;
3135 	int bufsize;
3136 	int namlen;
3137 	uint_t count;
3138 
3139 	vap = NULL;
3140 
3141 	vp = nfs3_fhtovp(&args->dir, exi);
3142 
3143 	DTRACE_NFSV3_4(op__readdir__start, struct svc_req *, req,
3144 	    cred_t *, cr, vnode_t *, vp, READDIR3args *, args);
3145 
3146 	if (vp == NULL) {
3147 		error = ESTALE;
3148 		goto out;
3149 	}
3150 
3151 	if (is_system_labeled()) {
3152 		bslabel_t *clabel = req->rq_label;
3153 
3154 		ASSERT(clabel != NULL);
3155 		DTRACE_PROBE2(tx__rfs3__log__info__opreaddir__clabel, char *,
3156 		    "got client label from request(1)", struct svc_req *, req);
3157 
3158 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
3159 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
3160 				resp->status = NFS3ERR_ACCES;
3161 				goto out1;
3162 			}
3163 		}
3164 	}
3165 
3166 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
3167 
3168 #ifdef DEBUG
3169 	if (rfs3_do_pre_op_attr) {
3170 		va.va_mask = AT_ALL;
3171 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3172 	} else
3173 		vap = NULL;
3174 #else
3175 	va.va_mask = AT_ALL;
3176 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3177 #endif
3178 
3179 	if (vp->v_type != VDIR) {
3180 		resp->status = NFS3ERR_NOTDIR;
3181 		goto out1;
3182 	}
3183 
3184 	error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
3185 	if (error)
3186 		goto out;
3187 
3188 	/*
3189 	 * Now don't allow arbitrary count to alloc;
3190 	 * allow the maximum not to exceed rfs3_tsize()
3191 	 */
3192 	if (args->count > rfs3_tsize(req))
3193 		args->count = rfs3_tsize(req);
3194 
3195 	/*
3196 	 * Make sure that there is room to read at least one entry
3197 	 * if any are available.
3198 	 */
3199 	if (args->count < DIRENT64_RECLEN(MAXNAMELEN))
3200 		count = DIRENT64_RECLEN(MAXNAMELEN);
3201 	else
3202 		count = args->count;
3203 
3204 	data = kmem_alloc(count, KM_SLEEP);
3205 
3206 	iov.iov_base = data;
3207 	iov.iov_len = count;
3208 	uio.uio_iov = &iov;
3209 	uio.uio_iovcnt = 1;
3210 	uio.uio_segflg = UIO_SYSSPACE;
3211 	uio.uio_extflg = UIO_COPY_CACHED;
3212 	uio.uio_loffset = (offset_t)args->cookie;
3213 	uio.uio_resid = count;
3214 
3215 	error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
3216 
3217 #ifdef DEBUG
3218 	if (rfs3_do_post_op_attr) {
3219 		va.va_mask = AT_ALL;
3220 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3221 	} else
3222 		vap = NULL;
3223 #else
3224 	va.va_mask = AT_ALL;
3225 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3226 #endif
3227 
3228 	if (error) {
3229 		kmem_free(data, count);
3230 		goto out;
3231 	}
3232 
3233 	/*
3234 	 * If the count was not large enough to be able to guarantee
3235 	 * to be able to return at least one entry, then need to
3236 	 * check to see if NFS3ERR_TOOSMALL should be returned.
3237 	 */
3238 	if (args->count < NFS3_READDIR_MIN_COUNT(MAXNAMELEN)) {
3239 		/*
3240 		 * bufsize is used to keep track of the size of the response.
3241 		 * It is primed with:
3242 		 *	1 for the status +
3243 		 *	1 for the dir_attributes.attributes boolean +
3244 		 *	2 for the cookie verifier
3245 		 * all times BYTES_PER_XDR_UNIT to convert from XDR units
3246 		 * to bytes.  If there are directory attributes to be
3247 		 * returned, then:
3248 		 *	NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
3249 		 * time BYTES_PER_XDR_UNIT is added to account for them.
3250 		 */
3251 		bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
3252 		if (vap != NULL)
3253 			bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
3254 		/*
3255 		 * An entry is composed of:
3256 		 *	1 for the true/false list indicator +
3257 		 *	2 for the fileid +
3258 		 *	1 for the length of the name +
3259 		 *	2 for the cookie +
3260 		 * all times BYTES_PER_XDR_UNIT to convert from
3261 		 * XDR units to bytes, plus the length of the name
3262 		 * rounded up to the nearest BYTES_PER_XDR_UNIT.
3263 		 */
3264 		if (count != uio.uio_resid) {
3265 			namlen = strlen(((struct dirent64 *)data)->d_name);
3266 			bufsize += (1 + 2 + 1 + 2) * BYTES_PER_XDR_UNIT +
3267 			    roundup(namlen, BYTES_PER_XDR_UNIT);
3268 		}
3269 		/*
3270 		 * We need to check to see if the number of bytes left
3271 		 * to go into the buffer will actually fit into the
3272 		 * buffer.  This is calculated as the size of this
3273 		 * entry plus:
3274 		 *	1 for the true/false list indicator +
3275 		 *	1 for the eof indicator
3276 		 * times BYTES_PER_XDR_UNIT to convert from from
3277 		 * XDR units to bytes.
3278 		 */
3279 		bufsize += (1 + 1) * BYTES_PER_XDR_UNIT;
3280 		if (bufsize > args->count) {
3281 			kmem_free(data, count);
3282 			resp->status = NFS3ERR_TOOSMALL;
3283 			goto out1;
3284 		}
3285 	}
3286 
3287 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3288 
3289 #if 0 /* notyet */
3290 	/*
3291 	 * Don't do this.  It causes local disk writes when just
3292 	 * reading the file and the overhead is deemed larger
3293 	 * than the benefit.
3294 	 */
3295 	/*
3296 	 * Force modified metadata out to stable storage.
3297 	 */
3298 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
3299 #endif
3300 
3301 	resp->status = NFS3_OK;
3302 	vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
3303 	resp->resok.cookieverf = 0;
3304 	resp->resok.reply.entries = (entry3 *)data;
3305 	resp->resok.reply.eof = iseof;
3306 	resp->resok.size = count - uio.uio_resid;
3307 	resp->resok.count = args->count;
3308 	resp->resok.freecount = count;
3309 
3310 	DTRACE_NFSV3_4(op__readdir__done, struct svc_req *, req,
3311 	    cred_t *, cr, vnode_t *, vp, READDIR3res *, resp);
3312 
3313 	VN_RELE(vp);
3314 
3315 	return;
3316 
3317 out:
3318 	if (curthread->t_flag & T_WOULDBLOCK) {
3319 		curthread->t_flag &= ~T_WOULDBLOCK;
3320 		resp->status = NFS3ERR_JUKEBOX;
3321 	} else
3322 		resp->status = puterrno3(error);
3323 out1:
3324 	DTRACE_NFSV3_4(op__readdir__done, struct svc_req *, req,
3325 	    cred_t *, cr, vnode_t *, vp, READDIR3res *, resp);
3326 
3327 	if (vp != NULL) {
3328 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3329 		VN_RELE(vp);
3330 	}
3331 	vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
3332 }
3333 
3334 void *
3335 rfs3_readdir_getfh(READDIR3args *args)
3336 {
3337 
3338 	return (&args->dir);
3339 }
3340 
3341 void
3342 rfs3_readdir_free(READDIR3res *resp)
3343 {
3344 
3345 	if (resp->status == NFS3_OK)
3346 		kmem_free(resp->resok.reply.entries, resp->resok.freecount);
3347 }
3348 
3349 #ifdef nextdp
3350 #undef nextdp
3351 #endif
3352 #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
3353 
3354 /*
3355  * This macro computes the size of a response which contains
3356  * one directory entry including the attributes as well as file handle.
3357  * If the incoming request is larger than this, then we are guaranteed to be
3358  * able to return at least one more directory entry if one exists.
3359  *
3360  * NFS3_READDIRPLUS_ENTRY is made up of the following:
3361  *
3362  * boolean - 1 * BYTES_PER_XDR_UNIT
3363  * file id - 2 * BYTES_PER_XDR_UNIT
3364  * directory name length - 1 * BYTES_PER_XDR_UNIT
3365  * cookie - 2 * BYTES_PER_XDR_UNIT
3366  * attribute flag - 1 * BYTES_PER_XDR_UNIT
3367  * attributes - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT
3368  * status byte for file handle - 1 *  BYTES_PER_XDR_UNIT
3369  * length of a file handle - 1 * BYTES_PER_XDR_UNIT
3370  * Maximum length of a file handle (NFS3_MAXFHSIZE)
3371  * name length of the entry to the nearest bytes
3372  */
3373 #define	NFS3_READDIRPLUS_ENTRY(namelen)	\
3374 	((1 + 2 + 1 + 2 + 1 + NFS3_SIZEOF_FATTR3 + 1 + 1) * \
3375 		BYTES_PER_XDR_UNIT + \
3376 	NFS3_MAXFHSIZE + roundup(namelen, BYTES_PER_XDR_UNIT))
3377 
3378 static int rfs3_readdir_unit = MAXBSIZE;
3379 
3380 /* ARGSUSED */
3381 void
3382 rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp,
3383 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
3384 {
3385 	int error;
3386 	vnode_t *vp;
3387 	struct vattr *vap;
3388 	struct vattr va;
3389 	struct iovec iov;
3390 	struct uio uio;
3391 	char *data;
3392 	int iseof;
3393 	struct dirent64 *dp;
3394 	vnode_t *nvp;
3395 	struct vattr *nvap;
3396 	struct vattr nva;
3397 	entryplus3_info *infop = NULL;
3398 	int size = 0;
3399 	int nents = 0;
3400 	int bufsize = 0;
3401 	int entrysize = 0;
3402 	int tofit = 0;
3403 	int rd_unit = rfs3_readdir_unit;
3404 	int prev_len;
3405 	int space_left;
3406 	int i;
3407 	uint_t *namlen = NULL;
3408 
3409 	vap = NULL;
3410 
3411 	vp = nfs3_fhtovp(&args->dir, exi);
3412 
3413 	DTRACE_NFSV3_4(op__readdirplus__start, struct svc_req *, req,
3414 	    cred_t *, cr, vnode_t *, vp, READDIRPLUS3args *, args);
3415 
3416 	if (vp == NULL) {
3417 		error = ESTALE;
3418 		goto out;
3419 	}
3420 
3421 	if (is_system_labeled()) {
3422 		bslabel_t *clabel = req->rq_label;
3423 
3424 		ASSERT(clabel != NULL);
3425 		DTRACE_PROBE2(tx__rfs3__log__info__opreaddirplus__clabel,
3426 		    char *, "got client label from request(1)",
3427 		    struct svc_req *, req);
3428 
3429 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
3430 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
3431 				resp->status = NFS3ERR_ACCES;
3432 				goto out1;
3433 			}
3434 		}
3435 	}
3436 
3437 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
3438 
3439 #ifdef DEBUG
3440 	if (rfs3_do_pre_op_attr) {
3441 		va.va_mask = AT_ALL;
3442 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3443 	} else
3444 		vap = NULL;
3445 #else
3446 	va.va_mask = AT_ALL;
3447 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3448 #endif
3449 
3450 	if (vp->v_type != VDIR) {
3451 		error = ENOTDIR;
3452 		goto out;
3453 	}
3454 
3455 	error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
3456 	if (error)
3457 		goto out;
3458 
3459 	/*
3460 	 * Don't allow arbitrary counts for allocation
3461 	 */
3462 	if (args->maxcount > rfs3_tsize(req))
3463 		args->maxcount = rfs3_tsize(req);
3464 
3465 	/*
3466 	 * Make sure that there is room to read at least one entry
3467 	 * if any are available
3468 	 */
3469 	args->dircount = MIN(args->dircount, args->maxcount);
3470 
3471 	if (args->dircount < DIRENT64_RECLEN(MAXNAMELEN))
3472 		args->dircount = DIRENT64_RECLEN(MAXNAMELEN);
3473 
3474 	/*
3475 	 * This allocation relies on a minimum directory entry
3476 	 * being roughly 24 bytes.  Therefore, the namlen array
3477 	 * will have enough space based on the maximum number of
3478 	 * entries to read.
3479 	 */
3480 	namlen = kmem_alloc(args->dircount, KM_SLEEP);
3481 
3482 	space_left = args->dircount;
3483 	data = kmem_alloc(args->dircount, KM_SLEEP);
3484 	dp = (struct dirent64 *)data;
3485 	uio.uio_iov = &iov;
3486 	uio.uio_iovcnt = 1;
3487 	uio.uio_segflg = UIO_SYSSPACE;
3488 	uio.uio_extflg = UIO_COPY_CACHED;
3489 	uio.uio_loffset = (offset_t)args->cookie;
3490 
3491 	/*
3492 	 * bufsize is used to keep track of the size of the response as we
3493 	 * get post op attributes and filehandles for each entry.  This is
3494 	 * an optimization as the server may have read more entries than will
3495 	 * fit in the buffer specified by maxcount.  We stop calculating
3496 	 * post op attributes and filehandles once we have exceeded maxcount.
3497 	 * This will minimize the effect of truncation.
3498 	 *
3499 	 * It is primed with:
3500 	 *	1 for the status +
3501 	 *	1 for the dir_attributes.attributes boolean +
3502 	 *	2 for the cookie verifier
3503 	 * all times BYTES_PER_XDR_UNIT to convert from XDR units
3504 	 * to bytes.  If there are directory attributes to be
3505 	 * returned, then:
3506 	 *	NFS3_SIZEOF_FATTR3 for the dir_attributes.attr fattr3
3507 	 * time BYTES_PER_XDR_UNIT is added to account for them.
3508 	 */
3509 	bufsize = (1 + 1 + 2) * BYTES_PER_XDR_UNIT;
3510 	if (vap != NULL)
3511 		bufsize += NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT;
3512 
3513 getmoredents:
3514 	/*
3515 	 * Here we make a check so that our read unit is not larger than
3516 	 * the space left in the buffer.
3517 	 */
3518 	rd_unit = MIN(rd_unit, space_left);
3519 	iov.iov_base = (char *)dp;
3520 	iov.iov_len = rd_unit;
3521 	uio.uio_resid = rd_unit;
3522 	prev_len = rd_unit;
3523 
3524 	error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
3525 
3526 	if (error) {
3527 		kmem_free(data, args->dircount);
3528 		goto out;
3529 	}
3530 
3531 	if (uio.uio_resid == prev_len && !iseof) {
3532 		if (nents == 0) {
3533 			kmem_free(data, args->dircount);
3534 			resp->status = NFS3ERR_TOOSMALL;
3535 			goto out1;
3536 		}
3537 
3538 		/*
3539 		 * We could not get any more entries, so get the attributes
3540 		 * and filehandle for the entries already obtained.
3541 		 */
3542 		goto good;
3543 	}
3544 
3545 	/*
3546 	 * We estimate the size of the response by assuming the
3547 	 * entry exists and attributes and filehandle are also valid
3548 	 */
3549 	for (size = prev_len - uio.uio_resid;
3550 	    size > 0;
3551 	    size -= dp->d_reclen, dp = nextdp(dp)) {
3552 
3553 		if (dp->d_ino == 0) {
3554 			nents++;
3555 			continue;
3556 		}
3557 
3558 		namlen[nents] = strlen(dp->d_name);
3559 		entrysize = NFS3_READDIRPLUS_ENTRY(namlen[nents]);
3560 
3561 		/*
3562 		 * We need to check to see if the number of bytes left
3563 		 * to go into the buffer will actually fit into the
3564 		 * buffer.  This is calculated as the size of this
3565 		 * entry plus:
3566 		 *	1 for the true/false list indicator +
3567 		 *	1 for the eof indicator
3568 		 * times BYTES_PER_XDR_UNIT to convert from XDR units
3569 		 * to bytes.
3570 		 *
3571 		 * Also check the dircount limit against the first entry read
3572 		 *
3573 		 */
3574 		tofit = entrysize + (1 + 1) * BYTES_PER_XDR_UNIT;
3575 		if (bufsize + tofit > args->maxcount) {
3576 			/*
3577 			 * We make a check here to see if this was the
3578 			 * first entry being measured.  If so, then maxcount
3579 			 * was too small to begin with and so we need to
3580 			 * return with NFS3ERR_TOOSMALL.
3581 			 */
3582 			if (nents == 0) {
3583 				kmem_free(data, args->dircount);
3584 				resp->status = NFS3ERR_TOOSMALL;
3585 				goto out1;
3586 			}
3587 			iseof = FALSE;
3588 			goto good;
3589 		}
3590 		bufsize += entrysize;
3591 		nents++;
3592 	}
3593 
3594 	/*
3595 	 * If there is enough room to fit at least 1 more entry including
3596 	 * post op attributes and filehandle in the buffer AND that we haven't
3597 	 * exceeded dircount then go back and get some more.
3598 	 */
3599 	if (!iseof &&
3600 	    (args->maxcount - bufsize) >= NFS3_READDIRPLUS_ENTRY(MAXNAMELEN)) {
3601 		space_left -= (prev_len - uio.uio_resid);
3602 		if (space_left >= DIRENT64_RECLEN(MAXNAMELEN))
3603 			goto getmoredents;
3604 
3605 		/* else, fall through */
3606 	}
3607 
3608 good:
3609 
3610 #ifdef DEBUG
3611 	if (rfs3_do_post_op_attr) {
3612 		va.va_mask = AT_ALL;
3613 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3614 	} else
3615 		vap = NULL;
3616 #else
3617 	va.va_mask = AT_ALL;
3618 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3619 #endif
3620 
3621 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3622 
3623 	infop = kmem_alloc(nents * sizeof (struct entryplus3_info), KM_SLEEP);
3624 	resp->resok.infop = infop;
3625 
3626 	dp = (struct dirent64 *)data;
3627 	for (i = 0; i < nents; i++) {
3628 
3629 		if (dp->d_ino == 0) {
3630 			infop[i].attr.attributes = FALSE;
3631 			infop[i].fh.handle_follows = FALSE;
3632 			dp = nextdp(dp);
3633 			continue;
3634 		}
3635 
3636 		infop[i].namelen = namlen[i];
3637 
3638 		error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr,
3639 		    NULL, NULL, NULL);
3640 		if (error) {
3641 			infop[i].attr.attributes = FALSE;
3642 			infop[i].fh.handle_follows = FALSE;
3643 			dp = nextdp(dp);
3644 			continue;
3645 		}
3646 
3647 #ifdef DEBUG
3648 		if (rfs3_do_post_op_attr) {
3649 			nva.va_mask = AT_ALL;
3650 			nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ?
3651 			    NULL : &nva;
3652 		} else
3653 			nvap = NULL;
3654 #else
3655 		nva.va_mask = AT_ALL;
3656 		nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva;
3657 #endif
3658 		vattr_to_post_op_attr(nvap, &infop[i].attr);
3659 
3660 #ifdef DEBUG
3661 		if (!rfs3_do_post_op_fh3)
3662 			infop[i].fh.handle_follows = FALSE;
3663 		else {
3664 #endif
3665 		error = makefh3(&infop[i].fh.handle, nvp, exi);
3666 		if (!error)
3667 			infop[i].fh.handle_follows = TRUE;
3668 		else
3669 			infop[i].fh.handle_follows = FALSE;
3670 #ifdef DEBUG
3671 		}
3672 #endif
3673 
3674 		VN_RELE(nvp);
3675 		dp = nextdp(dp);
3676 	}
3677 
3678 #if 0 /* notyet */
3679 	/*
3680 	 * Don't do this.  It causes local disk writes when just
3681 	 * reading the file and the overhead is deemed larger
3682 	 * than the benefit.
3683 	 */
3684 	/*
3685 	 * Force modified metadata out to stable storage.
3686 	 */
3687 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
3688 #endif
3689 
3690 	kmem_free(namlen, args->dircount);
3691 
3692 	resp->status = NFS3_OK;
3693 	vattr_to_post_op_attr(vap, &resp->resok.dir_attributes);
3694 	resp->resok.cookieverf = 0;
3695 	resp->resok.reply.entries = (entryplus3 *)data;
3696 	resp->resok.reply.eof = iseof;
3697 	resp->resok.size = nents;
3698 	resp->resok.count = args->dircount;
3699 	resp->resok.maxcount = args->maxcount;
3700 
3701 	DTRACE_NFSV3_4(op__readdirplus__done, struct svc_req *, req,
3702 	    cred_t *, cr, vnode_t *, vp, READDIRPLUS3res *, resp);
3703 
3704 	VN_RELE(vp);
3705 
3706 	return;
3707 
3708 out:
3709 	if (curthread->t_flag & T_WOULDBLOCK) {
3710 		curthread->t_flag &= ~T_WOULDBLOCK;
3711 		resp->status = NFS3ERR_JUKEBOX;
3712 	} else
3713 		resp->status = puterrno3(error);
3714 out1:
3715 	DTRACE_NFSV3_4(op__readdirplus__done, struct svc_req *, req,
3716 	    cred_t *, cr, vnode_t *, vp, READDIRPLUS3res *, resp);
3717 
3718 	if (vp != NULL) {
3719 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
3720 		VN_RELE(vp);
3721 	}
3722 
3723 	if (namlen != NULL)
3724 		kmem_free(namlen, args->dircount);
3725 
3726 	vattr_to_post_op_attr(vap, &resp->resfail.dir_attributes);
3727 }
3728 
3729 void *
3730 rfs3_readdirplus_getfh(READDIRPLUS3args *args)
3731 {
3732 
3733 	return (&args->dir);
3734 }
3735 
3736 void
3737 rfs3_readdirplus_free(READDIRPLUS3res *resp)
3738 {
3739 
3740 	if (resp->status == NFS3_OK) {
3741 		kmem_free(resp->resok.reply.entries, resp->resok.count);
3742 		kmem_free(resp->resok.infop,
3743 		    resp->resok.size * sizeof (struct entryplus3_info));
3744 	}
3745 }
3746 
3747 /* ARGSUSED */
3748 void
3749 rfs3_fsstat(FSSTAT3args *args, FSSTAT3res *resp, struct exportinfo *exi,
3750 	struct svc_req *req, cred_t *cr)
3751 {
3752 	int error;
3753 	vnode_t *vp;
3754 	struct vattr *vap;
3755 	struct vattr va;
3756 	struct statvfs64 sb;
3757 
3758 	vap = NULL;
3759 
3760 	vp = nfs3_fhtovp(&args->fsroot, exi);
3761 
3762 	DTRACE_NFSV3_4(op__fsstat__start, struct svc_req *, req,
3763 	    cred_t *, cr, vnode_t *, vp, FSSTAT3args *, args);
3764 
3765 	if (vp == NULL) {
3766 		error = ESTALE;
3767 		goto out;
3768 	}
3769 
3770 	if (is_system_labeled()) {
3771 		bslabel_t *clabel = req->rq_label;
3772 
3773 		ASSERT(clabel != NULL);
3774 		DTRACE_PROBE2(tx__rfs3__log__info__opfsstat__clabel, char *,
3775 		    "got client label from request(1)", struct svc_req *, req);
3776 
3777 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
3778 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
3779 				resp->status = NFS3ERR_ACCES;
3780 				goto out1;
3781 			}
3782 		}
3783 	}
3784 
3785 	error = VFS_STATVFS(vp->v_vfsp, &sb);
3786 
3787 #ifdef DEBUG
3788 	if (rfs3_do_post_op_attr) {
3789 		va.va_mask = AT_ALL;
3790 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3791 	} else
3792 		vap = NULL;
3793 #else
3794 	va.va_mask = AT_ALL;
3795 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3796 #endif
3797 
3798 	if (error)
3799 		goto out;
3800 
3801 	resp->status = NFS3_OK;
3802 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
3803 	if (sb.f_blocks != (fsblkcnt64_t)-1)
3804 		resp->resok.tbytes = (size3)sb.f_frsize * (size3)sb.f_blocks;
3805 	else
3806 		resp->resok.tbytes = (size3)sb.f_blocks;
3807 	if (sb.f_bfree != (fsblkcnt64_t)-1)
3808 		resp->resok.fbytes = (size3)sb.f_frsize * (size3)sb.f_bfree;
3809 	else
3810 		resp->resok.fbytes = (size3)sb.f_bfree;
3811 	if (sb.f_bavail != (fsblkcnt64_t)-1)
3812 		resp->resok.abytes = (size3)sb.f_frsize * (size3)sb.f_bavail;
3813 	else
3814 		resp->resok.abytes = (size3)sb.f_bavail;
3815 	resp->resok.tfiles = (size3)sb.f_files;
3816 	resp->resok.ffiles = (size3)sb.f_ffree;
3817 	resp->resok.afiles = (size3)sb.f_favail;
3818 	resp->resok.invarsec = 0;
3819 
3820 	DTRACE_NFSV3_4(op__fsstat__done, struct svc_req *, req,
3821 	    cred_t *, cr, vnode_t *, vp, FSSTAT3res *, resp);
3822 	VN_RELE(vp);
3823 
3824 	return;
3825 
3826 out:
3827 	if (curthread->t_flag & T_WOULDBLOCK) {
3828 		curthread->t_flag &= ~T_WOULDBLOCK;
3829 		resp->status = NFS3ERR_JUKEBOX;
3830 	} else
3831 		resp->status = puterrno3(error);
3832 out1:
3833 	DTRACE_NFSV3_4(op__fsstat__done, struct svc_req *, req,
3834 	    cred_t *, cr, vnode_t *, vp, FSSTAT3res *, resp);
3835 
3836 	if (vp != NULL)
3837 		VN_RELE(vp);
3838 	vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
3839 }
3840 
3841 void *
3842 rfs3_fsstat_getfh(FSSTAT3args *args)
3843 {
3844 
3845 	return (&args->fsroot);
3846 }
3847 
3848 /* ARGSUSED */
3849 void
3850 rfs3_fsinfo(FSINFO3args *args, FSINFO3res *resp, struct exportinfo *exi,
3851 	struct svc_req *req, cred_t *cr)
3852 {
3853 	vnode_t *vp;
3854 	struct vattr *vap;
3855 	struct vattr va;
3856 	uint32_t xfer_size;
3857 	ulong_t l = 0;
3858 	int error;
3859 
3860 	vp = nfs3_fhtovp(&args->fsroot, exi);
3861 
3862 	DTRACE_NFSV3_4(op__fsinfo__start, struct svc_req *, req,
3863 	    cred_t *, cr, vnode_t *, vp, FSINFO3args *, args);
3864 
3865 	if (vp == NULL) {
3866 		if (curthread->t_flag & T_WOULDBLOCK) {
3867 			curthread->t_flag &= ~T_WOULDBLOCK;
3868 			resp->status = NFS3ERR_JUKEBOX;
3869 		} else
3870 			resp->status = NFS3ERR_STALE;
3871 		vattr_to_post_op_attr(NULL, &resp->resfail.obj_attributes);
3872 		goto out;
3873 	}
3874 
3875 	if (is_system_labeled()) {
3876 		bslabel_t *clabel = req->rq_label;
3877 
3878 		ASSERT(clabel != NULL);
3879 		DTRACE_PROBE2(tx__rfs3__log__info__opfsinfo__clabel, char *,
3880 		    "got client label from request(1)", struct svc_req *, req);
3881 
3882 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
3883 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
3884 				resp->status = NFS3ERR_STALE;
3885 				vattr_to_post_op_attr(NULL,
3886 				    &resp->resfail.obj_attributes);
3887 				goto out;
3888 			}
3889 		}
3890 	}
3891 
3892 #ifdef DEBUG
3893 	if (rfs3_do_post_op_attr) {
3894 		va.va_mask = AT_ALL;
3895 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3896 	} else
3897 		vap = NULL;
3898 #else
3899 	va.va_mask = AT_ALL;
3900 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3901 #endif
3902 
3903 	resp->status = NFS3_OK;
3904 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
3905 	xfer_size = rfs3_tsize(req);
3906 	resp->resok.rtmax = xfer_size;
3907 	resp->resok.rtpref = xfer_size;
3908 	resp->resok.rtmult = DEV_BSIZE;
3909 	resp->resok.wtmax = xfer_size;
3910 	resp->resok.wtpref = xfer_size;
3911 	resp->resok.wtmult = DEV_BSIZE;
3912 	resp->resok.dtpref = MAXBSIZE;
3913 
3914 	/*
3915 	 * Large file spec: want maxfilesize based on limit of
3916 	 * underlying filesystem.  We can guess 2^31-1 if need be.
3917 	 */
3918 	error = VOP_PATHCONF(vp, _PC_FILESIZEBITS, &l, cr, NULL);
3919 
3920 	if (!error && l != 0 && l <= 64)
3921 		resp->resok.maxfilesize = (1LL << (l-1)) - 1;
3922 	else
3923 		resp->resok.maxfilesize = MAXOFF32_T;
3924 
3925 	resp->resok.time_delta.seconds = 0;
3926 	resp->resok.time_delta.nseconds = 1000;
3927 	resp->resok.properties = FSF3_LINK | FSF3_SYMLINK |
3928 	    FSF3_HOMOGENEOUS | FSF3_CANSETTIME;
3929 
3930 	DTRACE_NFSV3_4(op__fsinfo__done, struct svc_req *, req,
3931 	    cred_t *, cr, vnode_t *, vp, FSINFO3res *, resp);
3932 
3933 	VN_RELE(vp);
3934 
3935 	return;
3936 
3937 out:
3938 	DTRACE_NFSV3_4(op__fsinfo__done, struct svc_req *, req,
3939 	    cred_t *, cr, vnode_t *, NULL, FSINFO3res *, resp);
3940 	if (vp != NULL)
3941 		VN_RELE(vp);
3942 }
3943 
3944 void *
3945 rfs3_fsinfo_getfh(FSINFO3args *args)
3946 {
3947 
3948 	return (&args->fsroot);
3949 }
3950 
3951 /* ARGSUSED */
3952 void
3953 rfs3_pathconf(PATHCONF3args *args, PATHCONF3res *resp, struct exportinfo *exi,
3954 	struct svc_req *req, cred_t *cr)
3955 {
3956 	int error;
3957 	vnode_t *vp;
3958 	struct vattr *vap;
3959 	struct vattr va;
3960 	ulong_t val;
3961 
3962 	vap = NULL;
3963 
3964 	vp = nfs3_fhtovp(&args->object, exi);
3965 
3966 	DTRACE_NFSV3_4(op__pathconf__start, struct svc_req *, req,
3967 	    cred_t *, cr, vnode_t *, vp, PATHCONF3args *, args);
3968 
3969 	if (vp == NULL) {
3970 		error = ESTALE;
3971 		goto out;
3972 	}
3973 
3974 	if (is_system_labeled()) {
3975 		bslabel_t *clabel = req->rq_label;
3976 
3977 		ASSERT(clabel != NULL);
3978 		DTRACE_PROBE2(tx__rfs3__log__info__oppathconf__clabel, char *,
3979 		    "got client label from request(1)", struct svc_req *, req);
3980 
3981 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
3982 			if (!do_rfs_label_check(clabel, vp, DOMINANCE_CHECK)) {
3983 				resp->status = NFS3ERR_ACCES;
3984 				goto out1;
3985 			}
3986 		}
3987 	}
3988 
3989 #ifdef DEBUG
3990 	if (rfs3_do_post_op_attr) {
3991 		va.va_mask = AT_ALL;
3992 		vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3993 	} else
3994 		vap = NULL;
3995 #else
3996 	va.va_mask = AT_ALL;
3997 	vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va;
3998 #endif
3999 
4000 	error = VOP_PATHCONF(vp, _PC_LINK_MAX, &val, cr, NULL);
4001 	if (error)
4002 		goto out;
4003 	resp->resok.info.link_max = (uint32)val;
4004 
4005 	error = VOP_PATHCONF(vp, _PC_NAME_MAX, &val, cr, NULL);
4006 	if (error)
4007 		goto out;
4008 	resp->resok.info.name_max = (uint32)val;
4009 
4010 	error = VOP_PATHCONF(vp, _PC_NO_TRUNC, &val, cr, NULL);
4011 	if (error)
4012 		goto out;
4013 	if (val == 1)
4014 		resp->resok.info.no_trunc = TRUE;
4015 	else
4016 		resp->resok.info.no_trunc = FALSE;
4017 
4018 	error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &val, cr, NULL);
4019 	if (error)
4020 		goto out;
4021 	if (val == 1)
4022 		resp->resok.info.chown_restricted = TRUE;
4023 	else
4024 		resp->resok.info.chown_restricted = FALSE;
4025 
4026 	resp->status = NFS3_OK;
4027 	vattr_to_post_op_attr(vap, &resp->resok.obj_attributes);
4028 	resp->resok.info.case_insensitive = FALSE;
4029 	resp->resok.info.case_preserving = TRUE;
4030 	DTRACE_NFSV3_4(op__pathconf__done, struct svc_req *, req,
4031 	    cred_t *, cr, vnode_t *, vp, PATHCONF3res *, resp);
4032 	VN_RELE(vp);
4033 	return;
4034 
4035 out:
4036 	if (curthread->t_flag & T_WOULDBLOCK) {
4037 		curthread->t_flag &= ~T_WOULDBLOCK;
4038 		resp->status = NFS3ERR_JUKEBOX;
4039 	} else
4040 		resp->status = puterrno3(error);
4041 out1:
4042 	DTRACE_NFSV3_4(op__pathconf__done, struct svc_req *, req,
4043 	    cred_t *, cr, vnode_t *, vp, PATHCONF3res *, resp);
4044 	if (vp != NULL)
4045 		VN_RELE(vp);
4046 	vattr_to_post_op_attr(vap, &resp->resfail.obj_attributes);
4047 }
4048 
4049 void *
4050 rfs3_pathconf_getfh(PATHCONF3args *args)
4051 {
4052 
4053 	return (&args->object);
4054 }
4055 
4056 void
4057 rfs3_commit(COMMIT3args *args, COMMIT3res *resp, struct exportinfo *exi,
4058 	struct svc_req *req, cred_t *cr)
4059 {
4060 	int error;
4061 	vnode_t *vp;
4062 	struct vattr *bvap;
4063 	struct vattr bva;
4064 	struct vattr *avap;
4065 	struct vattr ava;
4066 
4067 	bvap = NULL;
4068 	avap = NULL;
4069 
4070 	vp = nfs3_fhtovp(&args->file, exi);
4071 
4072 	DTRACE_NFSV3_4(op__commit__start, struct svc_req *, req,
4073 	    cred_t *, cr, vnode_t *, vp, COMMIT3args *, args);
4074 
4075 	if (vp == NULL) {
4076 		error = ESTALE;
4077 		goto out;
4078 	}
4079 
4080 	bva.va_mask = AT_ALL;
4081 	error = VOP_GETATTR(vp, &bva, 0, cr, NULL);
4082 
4083 	/*
4084 	 * If we can't get the attributes, then we can't do the
4085 	 * right access checking.  So, we'll fail the request.
4086 	 */
4087 	if (error)
4088 		goto out;
4089 
4090 #ifdef DEBUG
4091 	if (rfs3_do_pre_op_attr)
4092 		bvap = &bva;
4093 	else
4094 		bvap = NULL;
4095 #else
4096 	bvap = &bva;
4097 #endif
4098 
4099 	if (rdonly(exi, req)) {
4100 		resp->status = NFS3ERR_ROFS;
4101 		goto out1;
4102 	}
4103 
4104 	if (vp->v_type != VREG) {
4105 		resp->status = NFS3ERR_INVAL;
4106 		goto out1;
4107 	}
4108 
4109 	if (is_system_labeled()) {
4110 		bslabel_t *clabel = req->rq_label;
4111 
4112 		ASSERT(clabel != NULL);
4113 		DTRACE_PROBE2(tx__rfs3__log__info__opcommit__clabel, char *,
4114 		    "got client label from request(1)", struct svc_req *, req);
4115 
4116 		if (!blequal(&l_admin_low->tsl_label, clabel)) {
4117 			if (!do_rfs_label_check(clabel, vp, EQUALITY_CHECK)) {
4118 				resp->status = NFS3ERR_ACCES;
4119 				goto out1;
4120 			}
4121 		}
4122 	}
4123 
4124 	if (crgetuid(cr) != bva.va_uid &&
4125 	    (error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL)))
4126 		goto out;
4127 
4128 	error = VOP_PUTPAGE(vp, args->offset, args->count, 0, cr, NULL);
4129 	if (!error)
4130 		error = VOP_FSYNC(vp, FNODSYNC, cr, NULL);
4131 
4132 #ifdef DEBUG
4133 	if (rfs3_do_post_op_attr) {
4134 		ava.va_mask = AT_ALL;
4135 		avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
4136 	} else
4137 		avap = NULL;
4138 #else
4139 	ava.va_mask = AT_ALL;
4140 	avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava;
4141 #endif
4142 
4143 	if (error)
4144 		goto out;
4145 
4146 	resp->status = NFS3_OK;
4147 	vattr_to_wcc_data(bvap, avap, &resp->resok.file_wcc);
4148 	resp->resok.verf = write3verf;
4149 
4150 	DTRACE_NFSV3_4(op__commit__done, struct svc_req *, req,
4151 	    cred_t *, cr, vnode_t *, vp, COMMIT3res *, resp);
4152 
4153 	VN_RELE(vp);
4154 
4155 	return;
4156 
4157 out:
4158 	if (curthread->t_flag & T_WOULDBLOCK) {
4159 		curthread->t_flag &= ~T_WOULDBLOCK;
4160 		resp->status = NFS3ERR_JUKEBOX;
4161 	} else
4162 		resp->status = puterrno3(error);
4163 out1:
4164 	DTRACE_NFSV3_4(op__commit__done, struct svc_req *, req,
4165 	    cred_t *, cr, vnode_t *, vp, COMMIT3res *, resp);
4166 
4167 	if (vp != NULL)
4168 		VN_RELE(vp);
4169 	vattr_to_wcc_data(bvap, avap, &resp->resfail.file_wcc);
4170 }
4171 
4172 void *
4173 rfs3_commit_getfh(COMMIT3args *args)
4174 {
4175 
4176 	return (&args->file);
4177 }
4178 
4179 static int
4180 sattr3_to_vattr(sattr3 *sap, struct vattr *vap)
4181 {
4182 
4183 	vap->va_mask = 0;
4184 
4185 	if (sap->mode.set_it) {
4186 		vap->va_mode = (mode_t)sap->mode.mode;
4187 		vap->va_mask |= AT_MODE;
4188 	}
4189 	if (sap->uid.set_it) {
4190 		vap->va_uid = (uid_t)sap->uid.uid;
4191 		vap->va_mask |= AT_UID;
4192 	}
4193 	if (sap->gid.set_it) {
4194 		vap->va_gid = (gid_t)sap->gid.gid;
4195 		vap->va_mask |= AT_GID;
4196 	}
4197 	if (sap->size.set_it) {
4198 		if (sap->size.size > (size3)((u_longlong_t)-1))
4199 			return (EINVAL);
4200 		vap->va_size = sap->size.size;
4201 		vap->va_mask |= AT_SIZE;
4202 	}
4203 	if (sap->atime.set_it == SET_TO_CLIENT_TIME) {
4204 #ifndef _LP64
4205 		/* check time validity */
4206 		if (!NFS3_TIME_OK(sap->atime.atime.seconds))
4207 			return (EOVERFLOW);
4208 #endif
4209 		/*
4210 		 * nfs protocol defines times as unsigned so don't extend sign,
4211 		 * unless sysadmin set nfs_allow_preepoch_time.
4212 		 */
4213 		NFS_TIME_T_CONVERT(vap->va_atime.tv_sec,
4214 		    sap->atime.atime.seconds);
4215 		vap->va_atime.tv_nsec = (uint32_t)sap->atime.atime.nseconds;
4216 		vap->va_mask |= AT_ATIME;
4217 	} else if (sap->atime.set_it == SET_TO_SERVER_TIME) {
4218 		gethrestime(&vap->va_atime);
4219 		vap->va_mask |= AT_ATIME;
4220 	}
4221 	if (sap->mtime.set_it == SET_TO_CLIENT_TIME) {
4222 #ifndef _LP64
4223 		/* check time validity */
4224 		if (!NFS3_TIME_OK(sap->mtime.mtime.seconds))
4225 			return (EOVERFLOW);
4226 #endif
4227 		/*
4228 		 * nfs protocol defines times as unsigned so don't extend sign,
4229 		 * unless sysadmin set nfs_allow_preepoch_time.
4230 		 */
4231 		NFS_TIME_T_CONVERT(vap->va_mtime.tv_sec,
4232 		    sap->mtime.mtime.seconds);
4233 		vap->va_mtime.tv_nsec = (uint32_t)sap->mtime.mtime.nseconds;
4234 		vap->va_mask |= AT_MTIME;
4235 	} else if (sap->mtime.set_it == SET_TO_SERVER_TIME) {
4236 		gethrestime(&vap->va_mtime);
4237 		vap->va_mask |= AT_MTIME;
4238 	}
4239 
4240 	return (0);
4241 }
4242 
4243 static ftype3 vt_to_nf3[] = {
4244 	0, NF3REG, NF3DIR, NF3BLK, NF3CHR, NF3LNK, NF3FIFO, 0, 0, NF3SOCK, 0
4245 };
4246 
4247 static int
4248 vattr_to_fattr3(struct vattr *vap, fattr3 *fap)
4249 {
4250 
4251 	ASSERT(vap->va_type >= VNON && vap->va_type <= VBAD);
4252 	/* Return error if time or size overflow */
4253 	if (! (NFS_VAP_TIME_OK(vap) && NFS3_SIZE_OK(vap->va_size))) {
4254 		return (EOVERFLOW);
4255 	}
4256 	fap->type = vt_to_nf3[vap->va_type];
4257 	fap->mode = (mode3)(vap->va_mode & MODEMASK);
4258 	fap->nlink = (uint32)vap->va_nlink;
4259 	if (vap->va_uid == UID_NOBODY)
4260 		fap->uid = (uid3)NFS_UID_NOBODY;
4261 	else
4262 		fap->uid = (uid3)vap->va_uid;
4263 	if (vap->va_gid == GID_NOBODY)
4264 		fap->gid = (gid3)NFS_GID_NOBODY;
4265 	else
4266 		fap->gid = (gid3)vap->va_gid;
4267 	fap->size = (size3)vap->va_size;
4268 	fap->used = (size3)DEV_BSIZE * (size3)vap->va_nblocks;
4269 	fap->rdev.specdata1 = (uint32)getmajor(vap->va_rdev);
4270 	fap->rdev.specdata2 = (uint32)getminor(vap->va_rdev);
4271 	fap->fsid = (uint64)vap->va_fsid;
4272 	fap->fileid = (fileid3)vap->va_nodeid;
4273 	fap->atime.seconds = vap->va_atime.tv_sec;
4274 	fap->atime.nseconds = vap->va_atime.tv_nsec;
4275 	fap->mtime.seconds = vap->va_mtime.tv_sec;
4276 	fap->mtime.nseconds = vap->va_mtime.tv_nsec;
4277 	fap->ctime.seconds = vap->va_ctime.tv_sec;
4278 	fap->ctime.nseconds = vap->va_ctime.tv_nsec;
4279 	return (0);
4280 }
4281 
4282 static int
4283 vattr_to_wcc_attr(struct vattr *vap, wcc_attr *wccap)
4284 {
4285 
4286 	/* Return error if time or size overflow */
4287 	if (!(NFS_TIME_T_OK(vap->va_mtime.tv_sec) &&
4288 	    NFS_TIME_T_OK(vap->va_ctime.tv_sec) &&
4289 	    NFS3_SIZE_OK(vap->va_size))) {
4290 		return (EOVERFLOW);
4291 	}
4292 	wccap->size = (size3)vap->va_size;
4293 	wccap->mtime.seconds = vap->va_mtime.tv_sec;
4294 	wccap->mtime.nseconds = vap->va_mtime.tv_nsec;
4295 	wccap->ctime.seconds = vap->va_ctime.tv_sec;
4296 	wccap->ctime.nseconds = vap->va_ctime.tv_nsec;
4297 	return (0);
4298 }
4299 
4300 static void
4301 vattr_to_pre_op_attr(struct vattr *vap, pre_op_attr *poap)
4302 {
4303 
4304 	/* don't return attrs if time overflow */
4305 	if ((vap != NULL) && !vattr_to_wcc_attr(vap, &poap->attr)) {
4306 		poap->attributes = TRUE;
4307 	} else
4308 		poap->attributes = FALSE;
4309 }
4310 
4311 void
4312 vattr_to_post_op_attr(struct vattr *vap, post_op_attr *poap)
4313 {
4314 
4315 	/* don't return attrs if time overflow */
4316 	if ((vap != NULL) && !vattr_to_fattr3(vap, &poap->attr)) {
4317 		poap->attributes = TRUE;
4318 	} else
4319 		poap->attributes = FALSE;
4320 }
4321 
4322 static void
4323 vattr_to_wcc_data(struct vattr *bvap, struct vattr *avap, wcc_data *wccp)
4324 {
4325 
4326 	vattr_to_pre_op_attr(bvap, &wccp->before);
4327 	vattr_to_post_op_attr(avap, &wccp->after);
4328 }
4329 
4330 void
4331 rfs3_srvrinit(void)
4332 {
4333 	struct rfs3_verf_overlay {
4334 		uint_t id; /* a "unique" identifier */
4335 		int ts; /* a unique timestamp */
4336 	} *verfp;
4337 	timestruc_t now;
4338 
4339 	/*
4340 	 * The following algorithm attempts to find a unique verifier
4341 	 * to be used as the write verifier returned from the server
4342 	 * to the client.  It is important that this verifier change
4343 	 * whenever the server reboots.  Of secondary importance, it
4344 	 * is important for the verifier to be unique between two
4345 	 * different servers.
4346 	 *
4347 	 * Thus, an attempt is made to use the system hostid and the
4348 	 * current time in seconds when the nfssrv kernel module is
4349 	 * loaded.  It is assumed that an NFS server will not be able
4350 	 * to boot and then to reboot in less than a second.  If the
4351 	 * hostid has not been set, then the current high resolution
4352 	 * time is used.  This will ensure different verifiers each
4353 	 * time the server reboots and minimize the chances that two
4354 	 * different servers will have the same verifier.
4355 	 */
4356 
4357 #ifndef	lint
4358 	/*
4359 	 * We ASSERT that this constant logic expression is
4360 	 * always true because in the past, it wasn't.
4361 	 */
4362 	ASSERT(sizeof (*verfp) <= sizeof (write3verf));
4363 #endif
4364 
4365 	gethrestime(&now);
4366 	verfp = (struct rfs3_verf_overlay *)&write3verf;
4367 	verfp->ts = (int)now.tv_sec;
4368 	verfp->id = (uint_t)nfs_atoi(hw_serial);
4369 
4370 	if (verfp->id == 0)
4371 		verfp->id = (uint_t)now.tv_nsec;
4372 
4373 	nfs3_srv_caller_id = fs_new_caller_id();
4374 
4375 }
4376 
4377 static int
4378 rdma_setup_read_data3(READ3args *args, READ3resok *rok)
4379 {
4380 	struct clist	*wcl;
4381 	int		data_len, avail_len, num;
4382 	count3		count = rok->count;
4383 
4384 	data_len = num = avail_len = 0;
4385 
4386 	wcl = args->wlist;
4387 	while (wcl != NULL) {
4388 		if (wcl->c_dmemhandle.mrc_rmr == 0)
4389 			break;
4390 
4391 		avail_len += wcl->c_len;
4392 		if (wcl->c_len < count) {
4393 			data_len += wcl->c_len;
4394 		} else {
4395 			/* Can make the rest chunks all 0-len */
4396 			data_len += count;
4397 			wcl->c_len = count;
4398 		}
4399 		count -= wcl->c_len;
4400 		num ++;
4401 		wcl = wcl->c_next;
4402 	}
4403 
4404 	/*
4405 	 * MUST fail if there are still more data
4406 	 */
4407 	if (count > 0) {
4408 		DTRACE_PROBE2(nfss__e__read3_wlist_fail,
4409 		    int, data_len, int, count);
4410 		return (FALSE);
4411 	}
4412 
4413 	wcl = args->wlist;
4414 	rok->count = data_len;
4415 	rok->wlist_len = data_len;
4416 	rok->wlist = wcl;
4417 
4418 	return (TRUE);
4419 }
4420 
4421 void
4422 rfs3_srvrfini(void)
4423 {
4424 	/* Nothing to do */
4425 }
4426