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 */
87char *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 */
98uchar_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 */
106uchar_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 */
116uchar_t acl_ss_call_type_v2[] = {
117	0, 0, 1, 0, 0, 0
118};
119
120static int nfs_acl_dup_cache(vsecattr_t *, vsecattr_t *);
121static void nfs_acl_dup_res(rnode_t *, vsecattr_t *);
122
123/* ARGSUSED */
124int
125acl_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 */
187int
188acl_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
237int
238acl_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 */
277int
278acl_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
335tryagain:
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
408static 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 */
419int
420acl_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 */
471char *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 */
482uchar_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 */
492uchar_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 */
500uchar_t acl_timer_type_v3[] = {
501	0, 0, 0, 0
502};
503
504/* ARGSUSED */
505int
506acl_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 */
569int
570acl_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
619int
620acl_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
687void
688nfs_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
702static int
703nfs_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
730static void
731nfs_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
809static void
810nfs_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