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