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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 #include <sys/cred.h>
33 #include <sys/proc.h>
34 #include <sys/user.h>
35 #include <sys/time.h>
36 #include <sys/vnode.h>
37 #include <sys/vfs.h>
38 #include <sys/file.h>
39 #include <sys/uio.h>
40 #include <sys/buf.h>
41 #include <sys/mman.h>
42 #include <sys/tiuser.h>
43 #include <sys/pathname.h>
44 #include <sys/dirent.h>
45 #include <sys/conf.h>
46 #include <sys/debug.h>
47 #include <sys/unistd.h>
48 #include <sys/vmsystm.h>
49 #include <sys/fcntl.h>
50 #include <sys/flock.h>
51 #include <sys/swap.h>
52 #include <sys/errno.h>
53 #include <sys/sysmacros.h>
54 #include <sys/disp.h>
55 #include <sys/kmem.h>
56 #include <sys/cmn_err.h>
57 #include <sys/vtrace.h>
58 #include <sys/pathconf.h>
59 #include <sys/dnlc.h>
60 #include <sys/acl.h>
61 
62 #include <rpc/types.h>
63 #include <rpc/auth.h>
64 #include <rpc/clnt.h>
65 #include <rpc/xdr.h>
66 #include <nfs/nfs.h>
67 #include <nfs/nfs_clnt.h>
68 #include <nfs/rnode.h>
69 #include <nfs/nfs_acl.h>
70 
71 #include <vm/hat.h>
72 #include <vm/as.h>
73 #include <vm/page.h>
74 #include <vm/pvn.h>
75 #include <vm/seg.h>
76 #include <vm/seg_map.h>
77 #include <vm/seg_kmem.h>
78 #include <vm/seg_vn.h>
79 #include <vm/rm.h>
80 
81 #include <fs/fs_subr.h>
82 
83 /*
84  * The order and contents of this structure must be kept in sync with that of
85  * aclreqcnt_v2_tmpl in nfs_stats.c
86  */
87 char *aclnames_v2[] = {
88 	"null", "getacl", "setacl", "getattr", "access", "getxattrdir"
89 };
90 
91 /*
92  * This table maps from NFS protocol number into call type.
93  * Zero means a "Lookup" type call
94  * One  means a "Read" type call
95  * Two  means a "Write" type call
96  * This is used to select a default time-out.
97  */
98 uchar_t acl_call_type_v2[] = {
99 	0, 0, 1, 0, 0, 0
100 };
101 
102 /*
103  * Similar table, but to determine which timer to use
104  * (only real reads and writes!)
105  */
106 uchar_t acl_timer_type_v2[] = {
107 	0, 0, 0, 0, 0, 0
108 };
109 
110 /*
111  * This table maps from acl operation into a call type
112  * for the semisoft mount option.
113  * Zero means do not repeat operation.
114  * One  means repeat.
115  */
116 uchar_t acl_ss_call_type_v2[] = {
117 	0, 0, 1, 0, 0, 0
118 };
119 
120 static int nfs_acl_dup_cache(vsecattr_t *, vsecattr_t *);
121 static void nfs_acl_dup_res(rnode_t *, vsecattr_t *);
122 
123 /* ARGSUSED */
124 int
125 acl_getacl2(vnode_t *vp, vsecattr_t *vsp, int flag, cred_t *cr)
126 {
127 	int error;
128 	GETACL2args args;
129 	GETACL2res res;
130 	int doqueue;
131 	vattr_t va;
132 	rnode_t *rp;
133 	failinfo_t fi;
134 	hrtime_t t;
135 
136 	rp = VTOR(vp);
137 	if (rp->r_secattr != NULL) {
138 		error = nfs_validate_caches(vp, cr);
139 		if (error)
140 			return (error);
141 		mutex_enter(&rp->r_statelock);
142 		if (rp->r_secattr != NULL) {
143 			if (nfs_acl_dup_cache(vsp, rp->r_secattr)) {
144 				mutex_exit(&rp->r_statelock);
145 				return (0);
146 			}
147 		}
148 		mutex_exit(&rp->r_statelock);
149 	}
150 
151 	args.mask = vsp->vsa_mask;
152 	args.fh = *VTOFH(vp);
153 	fi.vp = vp;
154 	fi.fhp = (caddr_t)&args.fh;
155 	fi.copyproc = nfscopyfh;
156 	fi.lookupproc = nfslookup;
157 	fi.xattrdirproc = acl_getxattrdir2;
158 
159 	res.resok.acl.vsa_aclentp = NULL;
160 	res.resok.acl.vsa_dfaclentp = NULL;
161 
162 	doqueue = 1;
163 
164 	t = gethrtime();
165 
166 	error = acl2call(VTOMI(vp), ACLPROC2_GETACL,
167 	    xdr_GETACL2args, (caddr_t)&args,
168 	    xdr_GETACL2res, (caddr_t)&res, cr,
169 	    &doqueue, &res.status, 0, &fi);
170 
171 	if (error)
172 		return (error);
173 
174 	error = geterrno(res.status);
175 	if (!error) {
176 		(void) nfs_cache_fattr(vp, &res.resok.attr, &va, t, cr);
177 		nfs_acl_dup_res(rp, &res.resok.acl);
178 		*vsp = res.resok.acl;
179 	} else {
180 		PURGE_STALE_FH(error, vp, cr);
181 	}
182 
183 	return (error);
184 }
185 
186 /* ARGSUSED */
187 int
188 acl_setacl2(vnode_t *vp, vsecattr_t *vsp, int flag, cred_t *cr)
189 {
190 	int error;
191 	SETACL2args args;
192 	SETACL2res res;
193 	int doqueue;
194 	vattr_t va;
195 	rnode_t *rp;
196 	hrtime_t t;
197 
198 	args.fh = *VTOFH(vp);
199 	args.acl = *vsp;
200 
201 	doqueue = 1;
202 
203 	t = gethrtime();
204 
205 	error = acl2call(VTOMI(vp), ACLPROC2_SETACL,
206 	    xdr_SETACL2args, (caddr_t)&args,
207 	    xdr_SETACL2res, (caddr_t)&res, cr,
208 	    &doqueue, &res.status, 0, NULL);
209 
210 	/*
211 	 * On success, adding the arguments to setsecattr into the cache have
212 	 * not proven adequate.  On error, we cannot depend on cache.
213 	 * Simply flush the cache to force the next getsecattr
214 	 * to go over the wire.
215 	 */
216 	rp = VTOR(vp);
217 	mutex_enter(&rp->r_statelock);
218 	if (rp->r_secattr != NULL) {
219 		nfs_acl_free(rp->r_secattr);
220 		rp->r_secattr = NULL;
221 	}
222 	mutex_exit(&rp->r_statelock);
223 
224 	if (error)
225 		return (error);
226 
227 	error = geterrno(res.status);
228 	if (!error) {
229 		(void) nfs_cache_fattr(vp, &res.resok.attr, &va, t, cr);
230 	} else {
231 		PURGE_STALE_FH(error, vp, cr);
232 	}
233 
234 	return (error);
235 }
236 
237 int
238 acl_getattr2_otw(vnode_t *vp, vattr_t *vap, cred_t *cr)
239 {
240 	int error;
241 	GETATTR2args args;
242 	GETATTR2res res;
243 	int doqueue;
244 	failinfo_t fi;
245 	hrtime_t t;
246 
247 	args.fh = *VTOFH(vp);
248 	fi.vp = vp;
249 	fi.fhp = (caddr_t)&args.fh;
250 	fi.copyproc = nfscopyfh;
251 	fi.lookupproc = nfslookup;
252 	fi.xattrdirproc = acl_getxattrdir2;
253 
254 	doqueue = 1;
255 
256 	t = gethrtime();
257 
258 	error = acl2call(VTOMI(vp), ACLPROC2_GETATTR,
259 	    xdr_GETATTR2args, (caddr_t)&args,
260 	    xdr_GETATTR2res, (caddr_t)&res, cr,
261 	    &doqueue, &res.status, 0, &fi);
262 
263 	if (error)
264 		return (error);
265 	error = geterrno(res.status);
266 
267 	if (!error) {
268 		error = nfs_cache_fattr(vp, &res.resok.attr, vap, t, cr);
269 	} else {
270 		PURGE_STALE_FH(error, vp, cr);
271 	}
272 
273 	return (error);
274 }
275 
276 /* ARGSUSED */
277 int
278 acl_access2(vnode_t *vp, int mode, int flags, cred_t *cr)
279 {
280 	int error;
281 	ACCESS2args args;
282 	ACCESS2res res;
283 	int doqueue;
284 	uint32 acc;
285 	rnode_t *rp;
286 	cred_t *cred, *ncr, *ncrfree = NULL;
287 	vattr_t va;
288 	failinfo_t fi;
289 	nfs_access_type_t cacc;
290 	hrtime_t t;
291 
292 	acc = 0;
293 	if (mode & VREAD)
294 		acc |= ACCESS2_READ;
295 	if (mode & VWRITE) {
296 		if (vn_is_readonly(vp) && !IS_DEVVP(vp))
297 			return (EROFS);
298 		if (vp->v_type == VDIR)
299 			acc |= ACCESS2_DELETE;
300 		acc |= ACCESS2_MODIFY | ACCESS2_EXTEND;
301 	}
302 	if (mode & VEXEC) {
303 		if (vp->v_type == VDIR)
304 			acc |= ACCESS2_LOOKUP;
305 		else
306 			acc |= ACCESS2_EXECUTE;
307 	}
308 
309 	rp = VTOR(vp);
310 	if (vp->v_type == VDIR) {
311 		args.access = ACCESS2_READ | ACCESS2_DELETE | ACCESS2_MODIFY |
312 		    ACCESS2_EXTEND | ACCESS2_LOOKUP;
313 	} else {
314 		args.access = ACCESS2_READ | ACCESS2_MODIFY | ACCESS2_EXTEND |
315 		    ACCESS2_EXECUTE;
316 	}
317 	args.fh = *VTOFH(vp);
318 	fi.vp = vp;
319 	fi.fhp = (caddr_t)&args.fh;
320 	fi.copyproc = nfscopyfh;
321 	fi.lookupproc = nfslookup;
322 	fi.xattrdirproc = acl_getxattrdir2;
323 
324 	cred = cr;
325 	/*
326 	 * ncr and ncrfree both initially
327 	 * point to the memory area returned
328 	 * by crnetadjust();
329 	 * ncrfree not NULL when exiting means
330 	 * that we need to release it
331 	 */
332 	ncr = crnetadjust(cred);
333 	ncrfree = ncr;
334 
335 tryagain:
336 	if (rp->r_acache != NULL) {
337 		cacc = nfs_access_check(rp, acc, cr);
338 		if (cacc == NFS_ACCESS_ALLOWED) {
339 			if (ncrfree != NULL)
340 				crfree(ncrfree);
341 			return (0);
342 		}
343 		if (cacc == NFS_ACCESS_DENIED) {
344 			/*
345 			 * If the cred can be adjusted, try again
346 			 * with the new cred.
347 			 */
348 			if (ncr != NULL) {
349 				cred = ncr;
350 				ncr = NULL;
351 				goto tryagain;
352 			}
353 			if (ncrfree != NULL)
354 				crfree(ncrfree);
355 			return (EACCES);
356 		}
357 	}
358 
359 	doqueue = 1;
360 
361 	t = gethrtime();
362 
363 	error = acl2call(VTOMI(vp), ACLPROC2_ACCESS,
364 	    xdr_ACCESS2args, (caddr_t)&args,
365 	    xdr_ACCESS2res, (caddr_t)&res, cred,
366 	    &doqueue, &res.status, 0, &fi);
367 
368 	if (error) {
369 		if (ncrfree != NULL)
370 			crfree(ncrfree);
371 		return (error);
372 	}
373 
374 	error = geterrno(res.status);
375 	if (!error) {
376 		(void) nfs_cache_fattr(vp, &res.resok.attr, &va, t, cr);
377 		nfs_access_cache(rp, args.access, res.resok.access, cred);
378 		/*
379 		 * we just cached results with cred; if cred is the
380 		 * adjusted credentials from crnetadjust, we do not want
381 		 * to release them before exiting: hence setting ncrfree
382 		 * to NULL
383 		 */
384 		if (cred != cr)
385 			ncrfree = NULL;
386 		if ((acc & res.resok.access) != acc) {
387 			/*
388 			 * If the cred can be adjusted, try again
389 			 * with the new cred.
390 			 */
391 			if (ncr != NULL) {
392 				cred = ncr;
393 				ncr = NULL;
394 				goto tryagain;
395 			}
396 			error = EACCES;
397 		}
398 	} else {
399 		PURGE_STALE_FH(error, vp, cr);
400 	}
401 
402 	if (ncrfree != NULL)
403 		crfree(ncrfree);
404 
405 	return (error);
406 }
407 
408 static int xattr_lookup_neg_cache = 1;
409 
410 /*
411  * Look up a hidden attribute directory over the wire; the vnode
412  * we start with could be a file or directory.  We have to be
413  * tricky in recording the name in the rnode r_path - we use the
414  * magic name XATTR_RPATH and rely on code in failover_lookup() to
415  * detect this and use this routine to do the same lookup on
416  * remapping.  DNLC is easier: slashes are legal, so we use
417  * XATTR_DIR_NAME as UFS does.
418  */
419 int
420 acl_getxattrdir2(vnode_t *vp, vnode_t **vpp, bool_t create, cred_t *cr,
421 	int rfscall_flags)
422 {
423 	int error;
424 	GETXATTRDIR2args args;
425 	GETXATTRDIR2res res;
426 	int doqueue;
427 	failinfo_t fi;
428 	hrtime_t t;
429 
430 	args.fh = *VTOFH(vp);
431 	args.create = create;
432 
433 	fi.vp = vp;
434 	fi.fhp = NULL;		/* no need to update, filehandle not copied */
435 	fi.copyproc = nfscopyfh;
436 	fi.lookupproc = nfslookup;
437 	fi.xattrdirproc = acl_getxattrdir2;
438 
439 	doqueue = 1;
440 
441 	t = gethrtime();
442 
443 	error = acl2call(VTOMI(vp), ACLPROC2_GETXATTRDIR,
444 	    xdr_GETXATTRDIR2args, (caddr_t)&args,
445 	    xdr_GETXATTRDIR2res, (caddr_t)&res, cr,
446 	    &doqueue, &res.status, rfscall_flags, &fi);
447 
448 	if (!error) {
449 		error = geterrno(res.status);
450 		if (!error) {
451 			*vpp = makenfsnode(&res.resok.fh, &res.resok.attr,
452 			    vp->v_vfsp, t, cr, VTOR(vp)->r_path, XATTR_RPATH);
453 			mutex_enter(&(*vpp)->v_lock);
454 			(*vpp)->v_flag |= V_XATTRDIR;
455 			mutex_exit(&(*vpp)->v_lock);
456 			if (!(rfscall_flags & RFSCALL_SOFT))
457 				dnlc_update(vp, XATTR_DIR_NAME, *vpp);
458 		} else {
459 			PURGE_STALE_FH(error, vp, cr);
460 			if (error == ENOENT && xattr_lookup_neg_cache)
461 				dnlc_enter(vp, XATTR_DIR_NAME, DNLC_NO_VNODE);
462 		}
463 	}
464 	return (error);
465 }
466 
467 /*
468  * The order and contents of this structure must be kept in sync with that of
469  * aclreqcnt_v3_tmpl in nfs_stats.c
470  */
471 char *aclnames_v3[] = {
472 	"null", "getacl", "setacl", "getxattrdir"
473 };
474 
475 /*
476  * This table maps from NFS protocol number into call type.
477  * Zero means a "Lookup" type call
478  * One  means a "Read" type call
479  * Two  means a "Write" type call
480  * This is used to select a default time-out.
481  */
482 uchar_t acl_call_type_v3[] = {
483 	0, 0, 1, 0
484 };
485 
486 /*
487  * This table maps from acl operation into a call type
488  * for the semisoft mount option.
489  * Zero means do not repeat operation.
490  * One  means repeat.
491  */
492 uchar_t acl_ss_call_type_v3[] = {
493 	0, 0, 1, 0
494 };
495 
496 /*
497  * Similar table, but to determine which timer to use
498  * (only real reads and writes!)
499  */
500 uchar_t acl_timer_type_v3[] = {
501 	0, 0, 0, 0
502 };
503 
504 /* ARGSUSED */
505 int
506 acl_getacl3(vnode_t *vp, vsecattr_t *vsp, int flag, cred_t *cr)
507 {
508 	int error;
509 	GETACL3args args;
510 	GETACL3res res;
511 	int doqueue;
512 	rnode_t *rp;
513 	failinfo_t fi;
514 	hrtime_t t;
515 
516 	rp = VTOR(vp);
517 	if (rp->r_secattr != NULL) {
518 		error = nfs3_validate_caches(vp, cr);
519 		if (error)
520 			return (error);
521 		mutex_enter(&rp->r_statelock);
522 		if (rp->r_secattr != NULL) {
523 			if (nfs_acl_dup_cache(vsp, rp->r_secattr)) {
524 				mutex_exit(&rp->r_statelock);
525 				return (0);
526 			}
527 		}
528 		mutex_exit(&rp->r_statelock);
529 	}
530 
531 	args.mask = vsp->vsa_mask;
532 	args.fh = *VTOFH3(vp);
533 	fi.vp = vp;
534 	fi.fhp = (caddr_t)&args.fh;
535 	fi.copyproc = nfs3copyfh;
536 	fi.lookupproc = nfs3lookup;
537 	fi.xattrdirproc = acl_getxattrdir3;
538 
539 	res.resok.acl.vsa_aclentp = NULL;
540 	res.resok.acl.vsa_dfaclentp = NULL;
541 
542 	doqueue = 1;
543 
544 	t = gethrtime();
545 
546 	error = acl3call(VTOMI(vp), ACLPROC3_GETACL,
547 	    xdr_GETACL3args, (caddr_t)&args,
548 	    xdr_GETACL3res, (caddr_t)&res, cr,
549 	    &doqueue, &res.status, 0, &fi);
550 
551 	if (error)
552 		return (error);
553 
554 	error = geterrno3(res.status);
555 
556 	if (!error) {
557 		nfs3_cache_post_op_attr(vp, &res.resok.attr, t, cr);
558 		nfs_acl_dup_res(rp, &res.resok.acl);
559 		*vsp = res.resok.acl;
560 	} else {
561 		nfs3_cache_post_op_attr(vp, &res.resfail.attr, t, cr);
562 		PURGE_STALE_FH(error, vp, cr);
563 	}
564 
565 	return (error);
566 }
567 
568 /* ARGSUSED */
569 int
570 acl_setacl3(vnode_t *vp, vsecattr_t *vsp, int flag, cred_t *cr)
571 {
572 	int error;
573 	SETACL3args args;
574 	SETACL3res res;
575 	rnode_t *rp;
576 	int doqueue;
577 	hrtime_t t;
578 
579 	args.fh = *VTOFH3(vp);
580 	args.acl = *vsp;
581 
582 	doqueue = 1;
583 
584 	t = gethrtime();
585 
586 	error = acl3call(VTOMI(vp), ACLPROC3_SETACL,
587 	    xdr_SETACL3args, (caddr_t)&args,
588 	    xdr_SETACL3res, (caddr_t)&res, cr,
589 	    &doqueue, &res.status, 0, NULL);
590 
591 	/*
592 	 * On success, adding the arguments to setsecattr into the cache have
593 	 * not proven adequate.  On error, we cannot depend on cache.
594 	 * Simply flush the cache to force the next getsecattr
595 	 * to go over the wire.
596 	 */
597 	rp = VTOR(vp);
598 	mutex_enter(&rp->r_statelock);
599 	if (rp->r_secattr != NULL) {
600 		nfs_acl_free(rp->r_secattr);
601 		rp->r_secattr = NULL;
602 	}
603 	mutex_exit(&rp->r_statelock);
604 
605 	if (error)
606 		return (error);
607 
608 	error = geterrno3(res.status);
609 	if (!error) {
610 		nfs3_cache_post_op_attr(vp, &res.resok.attr, t, cr);
611 	} else {
612 		nfs3_cache_post_op_attr(vp, &res.resfail.attr, t, cr);
613 		PURGE_STALE_FH(error, vp, cr);
614 	}
615 
616 	return (error);
617 }
618 
619 int
620 acl_getxattrdir3(vnode_t *vp, vnode_t **vpp, bool_t create, cred_t *cr,
621 	int rfscall_flags)
622 {
623 	int error;
624 	GETXATTRDIR3args args;
625 	GETXATTRDIR3res res;
626 	int doqueue;
627 	struct vattr vattr;
628 	vnode_t *nvp;
629 	failinfo_t fi;
630 	hrtime_t t;
631 
632 	args.fh = *VTOFH3(vp);
633 	args.create = create;
634 
635 	fi.vp = vp;
636 	fi.fhp = (caddr_t)&args.fh;
637 	fi.copyproc = nfs3copyfh;
638 	fi.lookupproc = nfs3lookup;
639 	fi.xattrdirproc = acl_getxattrdir3;
640 
641 	doqueue = 1;
642 
643 	t = gethrtime();
644 
645 	error = acl3call(VTOMI(vp), ACLPROC3_GETXATTRDIR,
646 	    xdr_GETXATTRDIR3args, (caddr_t)&args,
647 	    xdr_GETXATTRDIR3res, (caddr_t)&res, cr,
648 	    &doqueue, &res.status, rfscall_flags, &fi);
649 
650 	if (error)
651 		return (error);
652 
653 	error = geterrno3(res.status);
654 	if (!error) {
655 		if (res.resok.attr.attributes) {
656 			nvp = makenfs3node(&res.resok.fh,
657 			    &res.resok.attr.attr,
658 			    vp->v_vfsp, t, cr, VTOR(vp)->r_path, XATTR_RPATH);
659 		} else {
660 			nvp = makenfs3node(&res.resok.fh, NULL,
661 			    vp->v_vfsp, t, cr, VTOR(vp)->r_path, XATTR_RPATH);
662 			if (nvp->v_type == VNON) {
663 				vattr.va_mask = AT_TYPE;
664 				error = nfs3getattr(nvp, &vattr, cr);
665 				if (error) {
666 					VN_RELE(nvp);
667 					return (error);
668 				}
669 				nvp->v_type = vattr.va_type;
670 			}
671 		}
672 		mutex_enter(&nvp->v_lock);
673 		nvp->v_flag |= V_XATTRDIR;
674 		mutex_exit(&nvp->v_lock);
675 		if (!(rfscall_flags & RFSCALL_SOFT))
676 			dnlc_update(vp, XATTR_DIR_NAME, nvp);
677 		*vpp = nvp;
678 	} else {
679 		PURGE_STALE_FH(error, vp, cr);
680 		if (error == ENOENT && xattr_lookup_neg_cache)
681 			dnlc_enter(vp, XATTR_DIR_NAME, DNLC_NO_VNODE);
682 	}
683 
684 	return (error);
685 }
686 
687 void
688 nfs_acl_free(vsecattr_t *vsp)
689 {
690 
691 	if (vsp->vsa_aclentp != NULL) {
692 		kmem_free(vsp->vsa_aclentp, vsp->vsa_aclcnt *
693 		    sizeof (aclent_t));
694 	}
695 	if (vsp->vsa_dfaclentp != NULL) {
696 		kmem_free(vsp->vsa_dfaclentp, vsp->vsa_dfaclcnt *
697 		    sizeof (aclent_t));
698 	}
699 	kmem_free(vsp, sizeof (*vsp));
700 }
701 
702 static int
703 nfs_acl_dup_cache(vsecattr_t *vsp, vsecattr_t *rvsp)
704 {
705 	size_t aclsize;
706 
707 	if ((rvsp->vsa_mask & vsp->vsa_mask) != vsp->vsa_mask)
708 		return (0);
709 
710 	if (vsp->vsa_mask & VSA_ACL) {
711 		ASSERT(rvsp->vsa_mask & VSA_ACLCNT);
712 		aclsize = rvsp->vsa_aclcnt * sizeof (aclent_t);
713 		vsp->vsa_aclentp = kmem_alloc(aclsize, KM_SLEEP);
714 		bcopy(rvsp->vsa_aclentp, vsp->vsa_aclentp, aclsize);
715 	}
716 	if (vsp->vsa_mask & VSA_ACLCNT)
717 		vsp->vsa_aclcnt = rvsp->vsa_aclcnt;
718 	if (vsp->vsa_mask & VSA_DFACL) {
719 		ASSERT(rvsp->vsa_mask & VSA_DFACLCNT);
720 		aclsize = rvsp->vsa_dfaclcnt * sizeof (aclent_t);
721 		vsp->vsa_dfaclentp = kmem_alloc(aclsize, KM_SLEEP);
722 		bcopy(rvsp->vsa_dfaclentp, vsp->vsa_dfaclentp, aclsize);
723 	}
724 	if (vsp->vsa_mask & VSA_DFACLCNT)
725 		vsp->vsa_dfaclcnt = rvsp->vsa_dfaclcnt;
726 
727 	return (1);
728 }
729 
730 static void
731 nfs_acl_dup_res_impl(kmutex_t *statelock, vsecattr_t **rspp, vsecattr_t *vsp)
732 {
733 	size_t aclsize;
734 	vsecattr_t *rvsp;
735 
736 	mutex_enter(statelock);
737 	if (*rspp != NULL)
738 		rvsp = *rspp;
739 	else {
740 		rvsp = kmem_zalloc(sizeof (*rvsp), KM_NOSLEEP);
741 		if (rvsp == NULL) {
742 			mutex_exit(statelock);
743 			return;
744 		}
745 		*rspp = rvsp;
746 	}
747 
748 	if (vsp->vsa_mask & VSA_ACL) {
749 		if (rvsp->vsa_aclentp != NULL &&
750 		    rvsp->vsa_aclcnt != vsp->vsa_aclcnt) {
751 			aclsize = rvsp->vsa_aclcnt * sizeof (aclent_t);
752 			kmem_free(rvsp->vsa_aclentp, aclsize);
753 			rvsp->vsa_aclentp = NULL;
754 		}
755 		if (vsp->vsa_aclcnt > 0) {
756 			aclsize = vsp->vsa_aclcnt * sizeof (aclent_t);
757 			if (rvsp->vsa_aclentp == NULL) {
758 				rvsp->vsa_aclentp = kmem_alloc(aclsize,
759 				    KM_SLEEP);
760 			}
761 			bcopy(vsp->vsa_aclentp, rvsp->vsa_aclentp, aclsize);
762 		}
763 		rvsp->vsa_aclcnt = vsp->vsa_aclcnt;
764 		rvsp->vsa_mask |= VSA_ACL | VSA_ACLCNT;
765 	}
766 	if (vsp->vsa_mask & VSA_ACLCNT) {
767 		if (rvsp->vsa_aclentp != NULL &&
768 		    rvsp->vsa_aclcnt != vsp->vsa_aclcnt) {
769 			aclsize = rvsp->vsa_aclcnt * sizeof (aclent_t);
770 			kmem_free(rvsp->vsa_aclentp, aclsize);
771 			rvsp->vsa_aclentp = NULL;
772 			rvsp->vsa_mask &= ~VSA_ACL;
773 		}
774 		rvsp->vsa_aclcnt = vsp->vsa_aclcnt;
775 		rvsp->vsa_mask |= VSA_ACLCNT;
776 	}
777 	if (vsp->vsa_mask & VSA_DFACL) {
778 		if (rvsp->vsa_dfaclentp != NULL &&
779 		    rvsp->vsa_dfaclcnt != vsp->vsa_dfaclcnt) {
780 			aclsize = rvsp->vsa_dfaclcnt * sizeof (aclent_t);
781 			kmem_free(rvsp->vsa_dfaclentp, aclsize);
782 			rvsp->vsa_dfaclentp = NULL;
783 		}
784 		if (vsp->vsa_dfaclcnt > 0) {
785 			aclsize = vsp->vsa_dfaclcnt * sizeof (aclent_t);
786 			if (rvsp->vsa_dfaclentp == NULL) {
787 				rvsp->vsa_dfaclentp = kmem_alloc(aclsize,
788 				    KM_SLEEP);
789 			}
790 			bcopy(vsp->vsa_dfaclentp, rvsp->vsa_dfaclentp, aclsize);
791 		}
792 		rvsp->vsa_dfaclcnt = vsp->vsa_dfaclcnt;
793 		rvsp->vsa_mask |= VSA_DFACL | VSA_DFACLCNT;
794 	}
795 	if (vsp->vsa_mask & VSA_DFACLCNT) {
796 		if (rvsp->vsa_dfaclentp != NULL &&
797 		    rvsp->vsa_dfaclcnt != vsp->vsa_dfaclcnt) {
798 			aclsize = rvsp->vsa_dfaclcnt * sizeof (aclent_t);
799 			kmem_free(rvsp->vsa_dfaclentp, aclsize);
800 			rvsp->vsa_dfaclentp = NULL;
801 			rvsp->vsa_mask &= ~VSA_DFACL;
802 		}
803 		rvsp->vsa_dfaclcnt = vsp->vsa_dfaclcnt;
804 		rvsp->vsa_mask |= VSA_DFACLCNT;
805 	}
806 	mutex_exit(statelock);
807 }
808 
809 static void
810 nfs_acl_dup_res(rnode_t *rp, vsecattr_t *vsp)
811 {
812 	nfs_acl_dup_res_impl(&rp->r_statelock, &rp->r_secattr, vsp);
813 }
814