xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_acl.c (revision 12322:2d0b03fa06b5)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
2212164SMark.Shellenbaum@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
25789Sahrens #include <sys/types.h>
26789Sahrens #include <sys/param.h>
27789Sahrens #include <sys/time.h>
28789Sahrens #include <sys/systm.h>
29789Sahrens #include <sys/sysmacros.h>
30789Sahrens #include <sys/resource.h>
31789Sahrens #include <sys/vfs.h>
32789Sahrens #include <sys/vnode.h>
335331Samw #include <sys/sid.h>
34789Sahrens #include <sys/file.h>
35789Sahrens #include <sys/stat.h>
36789Sahrens #include <sys/kmem.h>
37789Sahrens #include <sys/cmn_err.h>
38789Sahrens #include <sys/errno.h>
39789Sahrens #include <sys/unistd.h>
401576Smarks #include <sys/sdt.h>
41789Sahrens #include <sys/fs/zfs.h>
42789Sahrens #include <sys/mode.h>
43789Sahrens #include <sys/policy.h>
44789Sahrens #include <sys/zfs_znode.h>
455331Samw #include <sys/zfs_fuid.h>
46789Sahrens #include <sys/zfs_acl.h>
47789Sahrens #include <sys/zfs_dir.h>
48789Sahrens #include <sys/zfs_vfsops.h>
49789Sahrens #include <sys/dmu.h>
505331Samw #include <sys/dnode.h>
51789Sahrens #include <sys/zap.h>
5211935SMark.Shellenbaum@Sun.COM #include <sys/sa.h>
53789Sahrens #include "fs/fs_subr.h"
54789Sahrens #include <acl/acl_common.h>
55789Sahrens 
56789Sahrens #define	ALLOW	ACE_ACCESS_ALLOWED_ACE_TYPE
57789Sahrens #define	DENY	ACE_ACCESS_DENIED_ACE_TYPE
585331Samw #define	MAX_ACE_TYPE	ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
597559SMark.Shellenbaum@Sun.COM #define	MIN_ACE_TYPE	ALLOW
60789Sahrens 
61789Sahrens #define	OWNING_GROUP		(ACE_GROUP|ACE_IDENTIFIER_GROUP)
62789Sahrens #define	EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \
63789Sahrens     ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE)
64789Sahrens #define	EVERYONE_DENY_MASK (ACE_WRITE_ACL|ACE_WRITE_OWNER | \
65789Sahrens     ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
66789Sahrens #define	OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \
67789Sahrens     ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
685331Samw 
695331Samw #define	ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \
705331Samw     ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \
715331Samw     ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \
725331Samw     ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE)
735331Samw 
748952SMark.Shellenbaum@Sun.COM #define	WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS)
758952SMark.Shellenbaum@Sun.COM #define	WRITE_MASK_ATTRS (ACE_WRITE_ACL|ACE_WRITE_OWNER|ACE_WRITE_ATTRIBUTES| \
768952SMark.Shellenbaum@Sun.COM     ACE_DELETE|ACE_DELETE_CHILD)
778952SMark.Shellenbaum@Sun.COM #define	WRITE_MASK (WRITE_MASK_DATA|WRITE_MASK_ATTRS)
78789Sahrens 
79789Sahrens #define	OGE_CLEAR	(ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
80789Sahrens     ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
81789Sahrens 
82789Sahrens #define	OKAY_MASK_BITS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
83789Sahrens     ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
84789Sahrens 
85789Sahrens #define	ALL_INHERIT	(ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE | \
865331Samw     ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE|ACE_INHERITED_ACE)
87789Sahrens 
886385Smarks #define	RESTRICTED_CLEAR	(ACE_WRITE_ACL|ACE_WRITE_OWNER)
89789Sahrens 
905331Samw #define	V4_ACL_WIDE_FLAGS (ZFS_ACL_AUTO_INHERIT|ZFS_ACL_DEFAULTED|\
915331Samw     ZFS_ACL_PROTECTED)
925331Samw 
935331Samw #define	ZFS_ACL_WIDE_FLAGS (V4_ACL_WIDE_FLAGS|ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|\
945331Samw     ZFS_ACL_OBJ_ACE)
955331Samw 
9610143STim.Haley@Sun.COM #define	ALL_MODE_EXECS (S_IXUSR | S_IXGRP | S_IXOTH)
9710143STim.Haley@Sun.COM 
985331Samw static uint16_t
995331Samw zfs_ace_v0_get_type(void *acep)
1005331Samw {
1015331Samw 	return (((zfs_oldace_t *)acep)->z_type);
1025331Samw }
1035331Samw 
1045331Samw static uint16_t
1055331Samw zfs_ace_v0_get_flags(void *acep)
1065331Samw {
1075331Samw 	return (((zfs_oldace_t *)acep)->z_flags);
1085331Samw }
1095331Samw 
1105331Samw static uint32_t
1115331Samw zfs_ace_v0_get_mask(void *acep)
1125331Samw {
1135331Samw 	return (((zfs_oldace_t *)acep)->z_access_mask);
1145331Samw }
1155331Samw 
1165331Samw static uint64_t
1175331Samw zfs_ace_v0_get_who(void *acep)
1185331Samw {
1195331Samw 	return (((zfs_oldace_t *)acep)->z_fuid);
1205331Samw }
1215331Samw 
1225331Samw static void
1235331Samw zfs_ace_v0_set_type(void *acep, uint16_t type)
1245331Samw {
1255331Samw 	((zfs_oldace_t *)acep)->z_type = type;
1265331Samw }
1275331Samw 
1285331Samw static void
1295331Samw zfs_ace_v0_set_flags(void *acep, uint16_t flags)
1305331Samw {
1315331Samw 	((zfs_oldace_t *)acep)->z_flags = flags;
1325331Samw }
1335331Samw 
1345331Samw static void
1355331Samw zfs_ace_v0_set_mask(void *acep, uint32_t mask)
1365331Samw {
1375331Samw 	((zfs_oldace_t *)acep)->z_access_mask = mask;
1385331Samw }
1395331Samw 
1405331Samw static void
1415331Samw zfs_ace_v0_set_who(void *acep, uint64_t who)
1425331Samw {
1435331Samw 	((zfs_oldace_t *)acep)->z_fuid = who;
1445331Samw }
1455331Samw 
1465331Samw /*ARGSUSED*/
1475331Samw static size_t
1485331Samw zfs_ace_v0_size(void *acep)
1495331Samw {
1505331Samw 	return (sizeof (zfs_oldace_t));
1515331Samw }
1525331Samw 
1535331Samw static size_t
1545331Samw zfs_ace_v0_abstract_size(void)
1555331Samw {
1565331Samw 	return (sizeof (zfs_oldace_t));
1575331Samw }
1585331Samw 
1595331Samw static int
1605331Samw zfs_ace_v0_mask_off(void)
1615331Samw {
1625331Samw 	return (offsetof(zfs_oldace_t, z_access_mask));
1635331Samw }
1645331Samw 
1655331Samw /*ARGSUSED*/
1665331Samw static int
1675331Samw zfs_ace_v0_data(void *acep, void **datap)
1685331Samw {
1695331Samw 	*datap = NULL;
1705331Samw 	return (0);
1715331Samw }
1725331Samw 
1735331Samw static acl_ops_t zfs_acl_v0_ops = {
1745331Samw 	zfs_ace_v0_get_mask,
1755331Samw 	zfs_ace_v0_set_mask,
1765331Samw 	zfs_ace_v0_get_flags,
1775331Samw 	zfs_ace_v0_set_flags,
1785331Samw 	zfs_ace_v0_get_type,
1795331Samw 	zfs_ace_v0_set_type,
1805331Samw 	zfs_ace_v0_get_who,
1815331Samw 	zfs_ace_v0_set_who,
1825331Samw 	zfs_ace_v0_size,
1835331Samw 	zfs_ace_v0_abstract_size,
1845331Samw 	zfs_ace_v0_mask_off,
1855331Samw 	zfs_ace_v0_data
1865331Samw };
1875331Samw 
1885331Samw static uint16_t
1895331Samw zfs_ace_fuid_get_type(void *acep)
1905331Samw {
1915331Samw 	return (((zfs_ace_hdr_t *)acep)->z_type);
1925331Samw }
1935331Samw 
1945331Samw static uint16_t
1955331Samw zfs_ace_fuid_get_flags(void *acep)
1965331Samw {
1975331Samw 	return (((zfs_ace_hdr_t *)acep)->z_flags);
1985331Samw }
1995331Samw 
2005331Samw static uint32_t
2015331Samw zfs_ace_fuid_get_mask(void *acep)
2025331Samw {
2035331Samw 	return (((zfs_ace_hdr_t *)acep)->z_access_mask);
2045331Samw }
2055331Samw 
2065331Samw static uint64_t
2075331Samw zfs_ace_fuid_get_who(void *args)
2085331Samw {
2095331Samw 	uint16_t entry_type;
2105331Samw 	zfs_ace_t *acep = args;
2115331Samw 
2125331Samw 	entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
213789Sahrens 
2145331Samw 	if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
2155331Samw 	    entry_type == ACE_EVERYONE)
2165331Samw 		return (-1);
2175331Samw 	return (((zfs_ace_t *)acep)->z_fuid);
2185331Samw }
2195331Samw 
2205331Samw static void
2215331Samw zfs_ace_fuid_set_type(void *acep, uint16_t type)
2225331Samw {
2235331Samw 	((zfs_ace_hdr_t *)acep)->z_type = type;
2245331Samw }
2255331Samw 
2265331Samw static void
2275331Samw zfs_ace_fuid_set_flags(void *acep, uint16_t flags)
2285331Samw {
2295331Samw 	((zfs_ace_hdr_t *)acep)->z_flags = flags;
2305331Samw }
2315331Samw 
2325331Samw static void
2335331Samw zfs_ace_fuid_set_mask(void *acep, uint32_t mask)
2345331Samw {
2355331Samw 	((zfs_ace_hdr_t *)acep)->z_access_mask = mask;
2365331Samw }
2375331Samw 
2385331Samw static void
2395331Samw zfs_ace_fuid_set_who(void *arg, uint64_t who)
2405331Samw {
2415331Samw 	zfs_ace_t *acep = arg;
2425331Samw 
2435331Samw 	uint16_t entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
2445331Samw 
2455331Samw 	if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
2465331Samw 	    entry_type == ACE_EVERYONE)
2475331Samw 		return;
2485331Samw 	acep->z_fuid = who;
2495331Samw }
2505331Samw 
2515331Samw static size_t
2525331Samw zfs_ace_fuid_size(void *acep)
2535331Samw {
2545331Samw 	zfs_ace_hdr_t *zacep = acep;
2555331Samw 	uint16_t entry_type;
2565331Samw 
2575331Samw 	switch (zacep->z_type) {
2585331Samw 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
2595331Samw 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
2605331Samw 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
2615331Samw 	case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
2625331Samw 		return (sizeof (zfs_object_ace_t));
2635331Samw 	case ALLOW:
2645331Samw 	case DENY:
2655331Samw 		entry_type =
2665331Samw 		    (((zfs_ace_hdr_t *)acep)->z_flags & ACE_TYPE_FLAGS);
2675331Samw 		if (entry_type == ACE_OWNER ||
2687328SMark.Shellenbaum@Sun.COM 		    entry_type == OWNING_GROUP ||
2695331Samw 		    entry_type == ACE_EVERYONE)
2705331Samw 			return (sizeof (zfs_ace_hdr_t));
2715331Samw 		/*FALLTHROUGH*/
2725331Samw 	default:
2735331Samw 		return (sizeof (zfs_ace_t));
2745331Samw 	}
2755331Samw }
2765331Samw 
2775331Samw static size_t
2785331Samw zfs_ace_fuid_abstract_size(void)
2795331Samw {
2805331Samw 	return (sizeof (zfs_ace_hdr_t));
2815331Samw }
2825331Samw 
2835331Samw static int
2845331Samw zfs_ace_fuid_mask_off(void)
2855331Samw {
2865331Samw 	return (offsetof(zfs_ace_hdr_t, z_access_mask));
2875331Samw }
2885331Samw 
2895331Samw static int
2905331Samw zfs_ace_fuid_data(void *acep, void **datap)
2915331Samw {
2925331Samw 	zfs_ace_t *zacep = acep;
2935331Samw 	zfs_object_ace_t *zobjp;
2945331Samw 
2955331Samw 	switch (zacep->z_hdr.z_type) {
2965331Samw 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
2975331Samw 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
2985331Samw 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
2995331Samw 	case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
3005331Samw 		zobjp = acep;
3015331Samw 		*datap = (caddr_t)zobjp + sizeof (zfs_ace_t);
3025331Samw 		return (sizeof (zfs_object_ace_t) - sizeof (zfs_ace_t));
3035331Samw 	default:
3045331Samw 		*datap = NULL;
3055331Samw 		return (0);
3065331Samw 	}
3075331Samw }
3085331Samw 
3095331Samw static acl_ops_t zfs_acl_fuid_ops = {
3105331Samw 	zfs_ace_fuid_get_mask,
3115331Samw 	zfs_ace_fuid_set_mask,
3125331Samw 	zfs_ace_fuid_get_flags,
3135331Samw 	zfs_ace_fuid_set_flags,
3145331Samw 	zfs_ace_fuid_get_type,
3155331Samw 	zfs_ace_fuid_set_type,
3165331Samw 	zfs_ace_fuid_get_who,
3175331Samw 	zfs_ace_fuid_set_who,
3185331Samw 	zfs_ace_fuid_size,
3195331Samw 	zfs_ace_fuid_abstract_size,
3205331Samw 	zfs_ace_fuid_mask_off,
3215331Samw 	zfs_ace_fuid_data
3225331Samw };
3235331Samw 
32411935SMark.Shellenbaum@Sun.COM /*
32511935SMark.Shellenbaum@Sun.COM  * The following three functions are provided for compatibility with
32611935SMark.Shellenbaum@Sun.COM  * older ZPL version in order to determine if the file use to have
32711935SMark.Shellenbaum@Sun.COM  * an external ACL and what version of ACL previously existed on the
32811935SMark.Shellenbaum@Sun.COM  * file.  Would really be nice to not need this, sigh.
32911935SMark.Shellenbaum@Sun.COM  */
33011935SMark.Shellenbaum@Sun.COM 
33111935SMark.Shellenbaum@Sun.COM uint64_t
33211935SMark.Shellenbaum@Sun.COM zfs_external_acl(znode_t *zp)
33311935SMark.Shellenbaum@Sun.COM {
33411935SMark.Shellenbaum@Sun.COM 	zfs_acl_phys_t acl_phys;
33511935SMark.Shellenbaum@Sun.COM 
33611935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa)
33711935SMark.Shellenbaum@Sun.COM 		return (0);
33811935SMark.Shellenbaum@Sun.COM 
33911935SMark.Shellenbaum@Sun.COM 	VERIFY(0 == sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zp->z_zfsvfs),
34011935SMark.Shellenbaum@Sun.COM 	    &acl_phys, sizeof (acl_phys)));
34111935SMark.Shellenbaum@Sun.COM 
34211935SMark.Shellenbaum@Sun.COM 	return (acl_phys.z_acl_extern_obj);
34311935SMark.Shellenbaum@Sun.COM }
34411935SMark.Shellenbaum@Sun.COM 
34511935SMark.Shellenbaum@Sun.COM /*
34611935SMark.Shellenbaum@Sun.COM  * Determine size of ACL in bytes
34711935SMark.Shellenbaum@Sun.COM  *
34811935SMark.Shellenbaum@Sun.COM  * This is more complicated than it should be since we have to deal
34911935SMark.Shellenbaum@Sun.COM  * with old external ACLs.
35011935SMark.Shellenbaum@Sun.COM  */
35111935SMark.Shellenbaum@Sun.COM static int
35211935SMark.Shellenbaum@Sun.COM zfs_acl_znode_info(znode_t *zp, int *aclsize, int *aclcount,
35311935SMark.Shellenbaum@Sun.COM     zfs_acl_phys_t *aclphys)
35411935SMark.Shellenbaum@Sun.COM {
35511935SMark.Shellenbaum@Sun.COM 	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
35611935SMark.Shellenbaum@Sun.COM 	uint64_t acl_count;
35711935SMark.Shellenbaum@Sun.COM 	int size;
35811935SMark.Shellenbaum@Sun.COM 	int error;
35911935SMark.Shellenbaum@Sun.COM 
36011935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa) {
36111935SMark.Shellenbaum@Sun.COM 		if ((error = sa_size(zp->z_sa_hdl, SA_ZPL_DACL_ACES(zfsvfs),
36211935SMark.Shellenbaum@Sun.COM 		    &size)) != 0)
36311935SMark.Shellenbaum@Sun.COM 			return (error);
36411935SMark.Shellenbaum@Sun.COM 		*aclsize = size;
36511935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DACL_COUNT(zfsvfs),
36611935SMark.Shellenbaum@Sun.COM 		    &acl_count, sizeof (acl_count))) != 0)
36711935SMark.Shellenbaum@Sun.COM 			return (error);
36811935SMark.Shellenbaum@Sun.COM 		*aclcount = acl_count;
36911935SMark.Shellenbaum@Sun.COM 	} else {
37011935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs),
37111935SMark.Shellenbaum@Sun.COM 		    aclphys, sizeof (*aclphys))) != 0)
37211935SMark.Shellenbaum@Sun.COM 			return (error);
37311935SMark.Shellenbaum@Sun.COM 
37411935SMark.Shellenbaum@Sun.COM 		if (aclphys->z_acl_version == ZFS_ACL_VERSION_INITIAL) {
37511935SMark.Shellenbaum@Sun.COM 			*aclsize = ZFS_ACL_SIZE(aclphys->z_acl_size);
37611935SMark.Shellenbaum@Sun.COM 			*aclcount = aclphys->z_acl_size;
37711935SMark.Shellenbaum@Sun.COM 		} else {
37811935SMark.Shellenbaum@Sun.COM 			*aclsize = aclphys->z_acl_size;
37911935SMark.Shellenbaum@Sun.COM 			*aclcount = aclphys->z_acl_count;
38011935SMark.Shellenbaum@Sun.COM 		}
38111935SMark.Shellenbaum@Sun.COM 	}
38211935SMark.Shellenbaum@Sun.COM 	return (0);
38311935SMark.Shellenbaum@Sun.COM }
38411935SMark.Shellenbaum@Sun.COM 
38511935SMark.Shellenbaum@Sun.COM int
38611935SMark.Shellenbaum@Sun.COM zfs_znode_acl_version(znode_t *zp)
38711935SMark.Shellenbaum@Sun.COM {
38811935SMark.Shellenbaum@Sun.COM 	zfs_acl_phys_t acl_phys;
38911935SMark.Shellenbaum@Sun.COM 
39011935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa) {
39111935SMark.Shellenbaum@Sun.COM 		return (ZFS_ACL_VERSION_FUID);
39211935SMark.Shellenbaum@Sun.COM 	} else {
39311935SMark.Shellenbaum@Sun.COM 		VERIFY(0 == sa_lookup(zp->z_sa_hdl,
39411935SMark.Shellenbaum@Sun.COM 		    SA_ZPL_ZNODE_ACL(zp->z_zfsvfs),
39511935SMark.Shellenbaum@Sun.COM 		    &acl_phys, sizeof (acl_phys)));
39611935SMark.Shellenbaum@Sun.COM 		return (acl_phys.z_acl_version);
39711935SMark.Shellenbaum@Sun.COM 	}
39811935SMark.Shellenbaum@Sun.COM }
39911935SMark.Shellenbaum@Sun.COM 
4005331Samw static int
4015331Samw zfs_acl_version(int version)
4025331Samw {
4035331Samw 	if (version < ZPL_VERSION_FUID)
4045331Samw 		return (ZFS_ACL_VERSION_INITIAL);
4055331Samw 	else
4065331Samw 		return (ZFS_ACL_VERSION_FUID);
4075331Samw }
4085331Samw 
4095331Samw static int
4105331Samw zfs_acl_version_zp(znode_t *zp)
4115331Samw {
4125331Samw 	return (zfs_acl_version(zp->z_zfsvfs->z_version));
4135331Samw }
414789Sahrens 
41511935SMark.Shellenbaum@Sun.COM zfs_acl_t *
4165331Samw zfs_acl_alloc(int vers)
417789Sahrens {
418789Sahrens 	zfs_acl_t *aclp;
419789Sahrens 
420789Sahrens 	aclp = kmem_zalloc(sizeof (zfs_acl_t), KM_SLEEP);
4215331Samw 	list_create(&aclp->z_acl, sizeof (zfs_acl_node_t),
4225331Samw 	    offsetof(zfs_acl_node_t, z_next));
4235331Samw 	aclp->z_version = vers;
4245331Samw 	if (vers == ZFS_ACL_VERSION_FUID)
4255331Samw 		aclp->z_ops = zfs_acl_fuid_ops;
4265331Samw 	else
4275331Samw 		aclp->z_ops = zfs_acl_v0_ops;
4285331Samw 	return (aclp);
4295331Samw }
4305331Samw 
43111935SMark.Shellenbaum@Sun.COM zfs_acl_node_t *
4325331Samw zfs_acl_node_alloc(size_t bytes)
4335331Samw {
4345331Samw 	zfs_acl_node_t *aclnode;
4355331Samw 
4365331Samw 	aclnode = kmem_zalloc(sizeof (zfs_acl_node_t), KM_SLEEP);
4375331Samw 	if (bytes) {
4385331Samw 		aclnode->z_acldata = kmem_alloc(bytes, KM_SLEEP);
4395331Samw 		aclnode->z_allocdata = aclnode->z_acldata;
4405331Samw 		aclnode->z_allocsize = bytes;
4415331Samw 		aclnode->z_size = bytes;
442789Sahrens 	}
4435331Samw 
4445331Samw 	return (aclnode);
4455331Samw }
4465331Samw 
4475331Samw static void
4485331Samw zfs_acl_node_free(zfs_acl_node_t *aclnode)
4495331Samw {
4505331Samw 	if (aclnode->z_allocsize)
4515331Samw 		kmem_free(aclnode->z_allocdata, aclnode->z_allocsize);
4525331Samw 	kmem_free(aclnode, sizeof (zfs_acl_node_t));
453789Sahrens }
454789Sahrens 
4555489Smarks static void
4565489Smarks zfs_acl_release_nodes(zfs_acl_t *aclp)
457789Sahrens {
4585331Samw 	zfs_acl_node_t *aclnode;
4595331Samw 
4605331Samw 	while (aclnode = list_head(&aclp->z_acl)) {
4615331Samw 		list_remove(&aclp->z_acl, aclnode);
4625331Samw 		zfs_acl_node_free(aclnode);
463789Sahrens 	}
4645489Smarks 	aclp->z_acl_count = 0;
4655489Smarks 	aclp->z_acl_bytes = 0;
4665489Smarks }
4675331Samw 
4685489Smarks void
4695489Smarks zfs_acl_free(zfs_acl_t *aclp)
4705489Smarks {
4715489Smarks 	zfs_acl_release_nodes(aclp);
4725331Samw 	list_destroy(&aclp->z_acl);
473789Sahrens 	kmem_free(aclp, sizeof (zfs_acl_t));
474789Sahrens }
475789Sahrens 
4765331Samw static boolean_t
4777559SMark.Shellenbaum@Sun.COM zfs_acl_valid_ace_type(uint_t type, uint_t flags)
4787559SMark.Shellenbaum@Sun.COM {
4797559SMark.Shellenbaum@Sun.COM 	uint16_t entry_type;
4807559SMark.Shellenbaum@Sun.COM 
4817559SMark.Shellenbaum@Sun.COM 	switch (type) {
4827559SMark.Shellenbaum@Sun.COM 	case ALLOW:
4837559SMark.Shellenbaum@Sun.COM 	case DENY:
4847559SMark.Shellenbaum@Sun.COM 	case ACE_SYSTEM_AUDIT_ACE_TYPE:
4857559SMark.Shellenbaum@Sun.COM 	case ACE_SYSTEM_ALARM_ACE_TYPE:
4867559SMark.Shellenbaum@Sun.COM 		entry_type = flags & ACE_TYPE_FLAGS;
4877559SMark.Shellenbaum@Sun.COM 		return (entry_type == ACE_OWNER ||
4887559SMark.Shellenbaum@Sun.COM 		    entry_type == OWNING_GROUP ||
4897559SMark.Shellenbaum@Sun.COM 		    entry_type == ACE_EVERYONE || entry_type == 0 ||
4907559SMark.Shellenbaum@Sun.COM 		    entry_type == ACE_IDENTIFIER_GROUP);
4917559SMark.Shellenbaum@Sun.COM 	default:
4927559SMark.Shellenbaum@Sun.COM 		if (type >= MIN_ACE_TYPE && type <= MAX_ACE_TYPE)
4937559SMark.Shellenbaum@Sun.COM 			return (B_TRUE);
4947559SMark.Shellenbaum@Sun.COM 	}
4957559SMark.Shellenbaum@Sun.COM 	return (B_FALSE);
4967559SMark.Shellenbaum@Sun.COM }
4977559SMark.Shellenbaum@Sun.COM 
4987559SMark.Shellenbaum@Sun.COM static boolean_t
4995331Samw zfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags)
500789Sahrens {
5015331Samw 	/*
5025331Samw 	 * first check type of entry
5035331Samw 	 */
5045331Samw 
5057559SMark.Shellenbaum@Sun.COM 	if (!zfs_acl_valid_ace_type(type, iflags))
5065331Samw 		return (B_FALSE);
5075331Samw 
5085331Samw 	switch (type) {
5095331Samw 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
5105331Samw 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
5115331Samw 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
5125331Samw 	case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
5135331Samw 		if (aclp->z_version < ZFS_ACL_VERSION_FUID)
5145331Samw 			return (B_FALSE);
5155331Samw 		aclp->z_hints |= ZFS_ACL_OBJ_ACE;
5165331Samw 	}
517789Sahrens 
5187559SMark.Shellenbaum@Sun.COM 	/*
5197559SMark.Shellenbaum@Sun.COM 	 * next check inheritance level flags
5207559SMark.Shellenbaum@Sun.COM 	 */
5217559SMark.Shellenbaum@Sun.COM 
5227057Smarks 	if (obj_type == VDIR &&
5237057Smarks 	    (iflags & (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)))
5245331Samw 		aclp->z_hints |= ZFS_INHERIT_ACE;
5255331Samw 
5265331Samw 	if (iflags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
5275331Samw 		if ((iflags & (ACE_FILE_INHERIT_ACE|
5285331Samw 		    ACE_DIRECTORY_INHERIT_ACE)) == 0) {
5295331Samw 			return (B_FALSE);
5305331Samw 		}
5315331Samw 	}
5325331Samw 
5335331Samw 	return (B_TRUE);
5345331Samw }
5355331Samw 
5365331Samw static void *
5375331Samw zfs_acl_next_ace(zfs_acl_t *aclp, void *start, uint64_t *who,
5385331Samw     uint32_t *access_mask, uint16_t *iflags, uint16_t *type)
5395331Samw {
5405331Samw 	zfs_acl_node_t *aclnode;
5415331Samw 
54211935SMark.Shellenbaum@Sun.COM 	ASSERT(aclp);
54311935SMark.Shellenbaum@Sun.COM 
5445331Samw 	if (start == NULL) {
5455331Samw 		aclnode = list_head(&aclp->z_acl);
5465331Samw 		if (aclnode == NULL)
5475331Samw 			return (NULL);
5485331Samw 
5495331Samw 		aclp->z_next_ace = aclnode->z_acldata;
5505331Samw 		aclp->z_curr_node = aclnode;
5515331Samw 		aclnode->z_ace_idx = 0;
5525331Samw 	}
5535331Samw 
5545331Samw 	aclnode = aclp->z_curr_node;
5555331Samw 
5565331Samw 	if (aclnode == NULL)
5575331Samw 		return (NULL);
5585331Samw 
5595331Samw 	if (aclnode->z_ace_idx >= aclnode->z_ace_count) {
5605331Samw 		aclnode = list_next(&aclp->z_acl, aclnode);
5615331Samw 		if (aclnode == NULL)
5625331Samw 			return (NULL);
5635331Samw 		else {
5645331Samw 			aclp->z_curr_node = aclnode;
5655331Samw 			aclnode->z_ace_idx = 0;
5665331Samw 			aclp->z_next_ace = aclnode->z_acldata;
5675331Samw 		}
5685331Samw 	}
5695331Samw 
5705331Samw 	if (aclnode->z_ace_idx < aclnode->z_ace_count) {
5715331Samw 		void *acep = aclp->z_next_ace;
5727559SMark.Shellenbaum@Sun.COM 		size_t ace_size;
5737559SMark.Shellenbaum@Sun.COM 
5747559SMark.Shellenbaum@Sun.COM 		/*
5757559SMark.Shellenbaum@Sun.COM 		 * Make sure we don't overstep our bounds
5767559SMark.Shellenbaum@Sun.COM 		 */
5777559SMark.Shellenbaum@Sun.COM 		ace_size = aclp->z_ops.ace_size(acep);
5787559SMark.Shellenbaum@Sun.COM 
5797559SMark.Shellenbaum@Sun.COM 		if (((caddr_t)acep + ace_size) >
5807559SMark.Shellenbaum@Sun.COM 		    ((caddr_t)aclnode->z_acldata + aclnode->z_size)) {
5817559SMark.Shellenbaum@Sun.COM 			return (NULL);
5827559SMark.Shellenbaum@Sun.COM 		}
5837559SMark.Shellenbaum@Sun.COM 
5845331Samw 		*iflags = aclp->z_ops.ace_flags_get(acep);
5855331Samw 		*type = aclp->z_ops.ace_type_get(acep);
5865331Samw 		*access_mask = aclp->z_ops.ace_mask_get(acep);
5875331Samw 		*who = aclp->z_ops.ace_who_get(acep);
5887559SMark.Shellenbaum@Sun.COM 		aclp->z_next_ace = (caddr_t)aclp->z_next_ace + ace_size;
5895331Samw 		aclnode->z_ace_idx++;
59011935SMark.Shellenbaum@Sun.COM 
5915331Samw 		return ((void *)acep);
5925331Samw 	}
5935331Samw 	return (NULL);
5945331Samw }
5955331Samw 
5965331Samw /*ARGSUSED*/
5975331Samw static uint64_t
5985331Samw zfs_ace_walk(void *datap, uint64_t cookie, int aclcnt,
5995331Samw     uint16_t *flags, uint16_t *type, uint32_t *mask)
6005331Samw {
6015331Samw 	zfs_acl_t *aclp = datap;
6025331Samw 	zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie;
6035331Samw 	uint64_t who;
6045331Samw 
6055331Samw 	acep = zfs_acl_next_ace(aclp, acep, &who, mask,
6065331Samw 	    flags, type);
6075331Samw 	return ((uint64_t)(uintptr_t)acep);
6085331Samw }
6095331Samw 
6105331Samw static zfs_acl_node_t *
6115331Samw zfs_acl_curr_node(zfs_acl_t *aclp)
6125331Samw {
6135331Samw 	ASSERT(aclp->z_curr_node);
6145331Samw 	return (aclp->z_curr_node);
6155331Samw }
6165331Samw 
6175331Samw /*
6185331Samw  * Copy ACE to internal ZFS format.
6195331Samw  * While processing the ACL each ACE will be validated for correctness.
6205331Samw  * ACE FUIDs will be created later.
6215331Samw  */
6225331Samw int
6239179SMark.Shellenbaum@Sun.COM zfs_copy_ace_2_fuid(zfsvfs_t *zfsvfs, vtype_t obj_type, zfs_acl_t *aclp,
62411935SMark.Shellenbaum@Sun.COM     void *datap, zfs_ace_t *z_acl, uint64_t aclcnt, size_t *size,
6259179SMark.Shellenbaum@Sun.COM     zfs_fuid_info_t **fuidp, cred_t *cr)
6265331Samw {
6275331Samw 	int i;
6285331Samw 	uint16_t entry_type;
6295331Samw 	zfs_ace_t *aceptr = z_acl;
6305331Samw 	ace_t *acep = datap;
6315331Samw 	zfs_object_ace_t *zobjacep;
6325331Samw 	ace_object_t *aceobjp;
6335331Samw 
6345331Samw 	for (i = 0; i != aclcnt; i++) {
6355331Samw 		aceptr->z_hdr.z_access_mask = acep->a_access_mask;
6365331Samw 		aceptr->z_hdr.z_flags = acep->a_flags;
6375331Samw 		aceptr->z_hdr.z_type = acep->a_type;
6385331Samw 		entry_type = aceptr->z_hdr.z_flags & ACE_TYPE_FLAGS;
6395331Samw 		if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP &&
6405824Smarks 		    entry_type != ACE_EVERYONE) {
6419179SMark.Shellenbaum@Sun.COM 			aceptr->z_fuid = zfs_fuid_create(zfsvfs, acep->a_who,
6429179SMark.Shellenbaum@Sun.COM 			    cr, (entry_type == 0) ?
6439179SMark.Shellenbaum@Sun.COM 			    ZFS_ACE_USER : ZFS_ACE_GROUP, fuidp);
6445824Smarks 		}
6455824Smarks 
6465331Samw 		/*
6475331Samw 		 * Make sure ACE is valid
6485331Samw 		 */
6495331Samw 		if (zfs_ace_valid(obj_type, aclp, aceptr->z_hdr.z_type,
6505331Samw 		    aceptr->z_hdr.z_flags) != B_TRUE)
6515331Samw 			return (EINVAL);
6525331Samw 
6535331Samw 		switch (acep->a_type) {
6545331Samw 		case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
6555331Samw 		case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
6565331Samw 		case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
6575331Samw 		case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
6585331Samw 			zobjacep = (zfs_object_ace_t *)aceptr;
6595331Samw 			aceobjp = (ace_object_t *)acep;
6605331Samw 
6615331Samw 			bcopy(aceobjp->a_obj_type, zobjacep->z_object_type,
6625331Samw 			    sizeof (aceobjp->a_obj_type));
6635331Samw 			bcopy(aceobjp->a_inherit_obj_type,
6645331Samw 			    zobjacep->z_inherit_type,
6655331Samw 			    sizeof (aceobjp->a_inherit_obj_type));
6665331Samw 			acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t));
6675331Samw 			break;
6685331Samw 		default:
6695331Samw 			acep = (ace_t *)((caddr_t)acep + sizeof (ace_t));
6705331Samw 		}
6715331Samw 
6725331Samw 		aceptr = (zfs_ace_t *)((caddr_t)aceptr +
6735331Samw 		    aclp->z_ops.ace_size(aceptr));
6745331Samw 	}
6755331Samw 
6765331Samw 	*size = (caddr_t)aceptr - (caddr_t)z_acl;
677789Sahrens 
6785331Samw 	return (0);
6795331Samw }
6805331Samw 
6815331Samw /*
6825331Samw  * Copy ZFS ACEs to fixed size ace_t layout
6835331Samw  */
6845331Samw static void
6855771Sjp151216 zfs_copy_fuid_2_ace(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, cred_t *cr,
6865771Sjp151216     void *datap, int filter)
6875331Samw {
6885331Samw 	uint64_t who;
6895331Samw 	uint32_t access_mask;
6905331Samw 	uint16_t iflags, type;
6915331Samw 	zfs_ace_hdr_t *zacep = NULL;
6925331Samw 	ace_t *acep = datap;
6935331Samw 	ace_object_t *objacep;
6945331Samw 	zfs_object_ace_t *zobjacep;
6955331Samw 	size_t ace_size;
6965331Samw 	uint16_t entry_type;
6975331Samw 
6985331Samw 	while (zacep = zfs_acl_next_ace(aclp, zacep,
6995331Samw 	    &who, &access_mask, &iflags, &type)) {
7005331Samw 
7015331Samw 		switch (type) {
7025331Samw 		case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
7035331Samw 		case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
7045331Samw 		case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
7055331Samw 		case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
7065331Samw 			if (filter) {
7075331Samw 				continue;
7085331Samw 			}
7095331Samw 			zobjacep = (zfs_object_ace_t *)zacep;
7105331Samw 			objacep = (ace_object_t *)acep;
7115331Samw 			bcopy(zobjacep->z_object_type,
7125331Samw 			    objacep->a_obj_type,
7135331Samw 			    sizeof (zobjacep->z_object_type));
7145331Samw 			bcopy(zobjacep->z_inherit_type,
7155331Samw 			    objacep->a_inherit_obj_type,
7165331Samw 			    sizeof (zobjacep->z_inherit_type));
7175331Samw 			ace_size = sizeof (ace_object_t);
7185331Samw 			break;
7195331Samw 		default:
7205331Samw 			ace_size = sizeof (ace_t);
7215331Samw 			break;
7225331Samw 		}
7235331Samw 
7245331Samw 		entry_type = (iflags & ACE_TYPE_FLAGS);
7255331Samw 		if ((entry_type != ACE_OWNER &&
7267328SMark.Shellenbaum@Sun.COM 		    entry_type != OWNING_GROUP &&
7275959Smarks 		    entry_type != ACE_EVERYONE)) {
7285959Smarks 			acep->a_who = zfs_fuid_map_id(zfsvfs, who,
7295959Smarks 			    cr, (entry_type & ACE_IDENTIFIER_GROUP) ?
7305959Smarks 			    ZFS_ACE_GROUP : ZFS_ACE_USER);
7315959Smarks 		} else {
7325331Samw 			acep->a_who = (uid_t)(int64_t)who;
7335959Smarks 		}
7345331Samw 		acep->a_access_mask = access_mask;
7355331Samw 		acep->a_flags = iflags;
7365331Samw 		acep->a_type = type;
7375331Samw 		acep = (ace_t *)((caddr_t)acep + ace_size);
7385331Samw 	}
7395331Samw }
7405331Samw 
7415331Samw static int
7425331Samw zfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep,
7435331Samw     zfs_oldace_t *z_acl, int aclcnt, size_t *size)
7445331Samw {
7455331Samw 	int i;
7465331Samw 	zfs_oldace_t *aceptr = z_acl;
7475331Samw 
7485331Samw 	for (i = 0; i != aclcnt; i++, aceptr++) {
7495331Samw 		aceptr->z_access_mask = acep[i].a_access_mask;
7505331Samw 		aceptr->z_type = acep[i].a_type;
7515331Samw 		aceptr->z_flags = acep[i].a_flags;
7525331Samw 		aceptr->z_fuid = acep[i].a_who;
7535331Samw 		/*
7545331Samw 		 * Make sure ACE is valid
7555331Samw 		 */
7565331Samw 		if (zfs_ace_valid(obj_type, aclp, aceptr->z_type,
7575331Samw 		    aceptr->z_flags) != B_TRUE)
7585331Samw 			return (EINVAL);
7595331Samw 	}
7605331Samw 	*size = (caddr_t)aceptr - (caddr_t)z_acl;
7615331Samw 	return (0);
7625331Samw }
7635331Samw 
7645331Samw /*
7655331Samw  * convert old ACL format to new
7665331Samw  */
7675331Samw void
7689179SMark.Shellenbaum@Sun.COM zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp, cred_t *cr)
7695331Samw {
7705331Samw 	zfs_oldace_t *oldaclp;
7715331Samw 	int i;
7725331Samw 	uint16_t type, iflags;
7735331Samw 	uint32_t access_mask;
7745331Samw 	uint64_t who;
7755331Samw 	void *cookie = NULL;
7765489Smarks 	zfs_acl_node_t *newaclnode;
7775331Samw 
7785331Samw 	ASSERT(aclp->z_version == ZFS_ACL_VERSION_INITIAL);
7795331Samw 	/*
7805331Samw 	 * First create the ACE in a contiguous piece of memory
7815331Samw 	 * for zfs_copy_ace_2_fuid().
7825331Samw 	 *
7835331Samw 	 * We only convert an ACL once, so this won't happen
7845331Samw 	 * everytime.
7855331Samw 	 */
7865331Samw 	oldaclp = kmem_alloc(sizeof (zfs_oldace_t) * aclp->z_acl_count,
7875331Samw 	    KM_SLEEP);
7885331Samw 	i = 0;
7895331Samw 	while (cookie = zfs_acl_next_ace(aclp, cookie, &who,
7905331Samw 	    &access_mask, &iflags, &type)) {
7915331Samw 		oldaclp[i].z_flags = iflags;
7925331Samw 		oldaclp[i].z_type = type;
7935331Samw 		oldaclp[i].z_fuid = who;
7945331Samw 		oldaclp[i++].z_access_mask = access_mask;
7955331Samw 	}
7965331Samw 
7975331Samw 	newaclnode = zfs_acl_node_alloc(aclp->z_acl_count *
7985331Samw 	    sizeof (zfs_object_ace_t));
7995331Samw 	aclp->z_ops = zfs_acl_fuid_ops;
8009179SMark.Shellenbaum@Sun.COM 	VERIFY(zfs_copy_ace_2_fuid(zp->z_zfsvfs, ZTOV(zp)->v_type, aclp,
8019179SMark.Shellenbaum@Sun.COM 	    oldaclp, newaclnode->z_acldata, aclp->z_acl_count,
8029179SMark.Shellenbaum@Sun.COM 	    &newaclnode->z_size, NULL, cr) == 0);
8035331Samw 	newaclnode->z_ace_count = aclp->z_acl_count;
8045331Samw 	aclp->z_version = ZFS_ACL_VERSION;
8055331Samw 	kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t));
8065331Samw 
8075331Samw 	/*
8085331Samw 	 * Release all previous ACL nodes
8095331Samw 	 */
8105331Samw 
8115489Smarks 	zfs_acl_release_nodes(aclp);
8125489Smarks 
8135331Samw 	list_insert_head(&aclp->z_acl, newaclnode);
8145489Smarks 
8155489Smarks 	aclp->z_acl_bytes = newaclnode->z_size;
8165489Smarks 	aclp->z_acl_count = newaclnode->z_ace_count;
8175489Smarks 
818789Sahrens }
819789Sahrens 
820789Sahrens /*
821789Sahrens  * Convert unix access mask to v4 access mask
822789Sahrens  */
823789Sahrens static uint32_t
824789Sahrens zfs_unix_to_v4(uint32_t access_mask)
825789Sahrens {
826789Sahrens 	uint32_t new_mask = 0;
827789Sahrens 
8285331Samw 	if (access_mask & S_IXOTH)
8295331Samw 		new_mask |= ACE_EXECUTE;
8305331Samw 	if (access_mask & S_IWOTH)
8315331Samw 		new_mask |= ACE_WRITE_DATA;
8325331Samw 	if (access_mask & S_IROTH)
833789Sahrens 		new_mask |= ACE_READ_DATA;
834789Sahrens 	return (new_mask);
835789Sahrens }
836789Sahrens 
837789Sahrens static void
8385331Samw zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
8395331Samw     uint16_t access_type, uint64_t fuid, uint16_t entry_type)
840789Sahrens {
8415331Samw 	uint16_t type = entry_type & ACE_TYPE_FLAGS;
8425331Samw 
8435331Samw 	aclp->z_ops.ace_mask_set(acep, access_mask);
8445331Samw 	aclp->z_ops.ace_type_set(acep, access_type);
8455331Samw 	aclp->z_ops.ace_flags_set(acep, entry_type);
8467328SMark.Shellenbaum@Sun.COM 	if ((type != ACE_OWNER && type != OWNING_GROUP &&
8475331Samw 	    type != ACE_EVERYONE))
8485331Samw 		aclp->z_ops.ace_who_set(acep, fuid);
849789Sahrens }
850789Sahrens 
8515331Samw /*
8525331Samw  * Determine mode of file based on ACL.
8535331Samw  * Also, create FUIDs for any User/Group ACEs
8545331Samw  */
85511935SMark.Shellenbaum@Sun.COM uint64_t
85612164SMark.Shellenbaum@Sun.COM zfs_mode_compute(uint64_t fmode, zfs_acl_t *aclp,
85712164SMark.Shellenbaum@Sun.COM     uint64_t *pflags, uint64_t fuid, uint64_t fgid)
858789Sahrens {
8595331Samw 	int		entry_type;
8605331Samw 	mode_t		mode;
8615331Samw 	mode_t		seen = 0;
8625331Samw 	zfs_ace_hdr_t 	*acep = NULL;
8635331Samw 	uint64_t	who;
8645331Samw 	uint16_t	iflags, type;
8655331Samw 	uint32_t	access_mask;
8669981STim.Haley@Sun.COM 	boolean_t	an_exec_denied = B_FALSE;
867789Sahrens 
86811935SMark.Shellenbaum@Sun.COM 	mode = (fmode & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX));
8695331Samw 
8705331Samw 	while (acep = zfs_acl_next_ace(aclp, acep, &who,
8715331Samw 	    &access_mask, &iflags, &type)) {
8724869Smarks 
8737559SMark.Shellenbaum@Sun.COM 		if (!zfs_acl_valid_ace_type(type, iflags))
8747559SMark.Shellenbaum@Sun.COM 			continue;
8757559SMark.Shellenbaum@Sun.COM 
8767328SMark.Shellenbaum@Sun.COM 		entry_type = (iflags & ACE_TYPE_FLAGS);
8777328SMark.Shellenbaum@Sun.COM 
8784869Smarks 		/*
8797328SMark.Shellenbaum@Sun.COM 		 * Skip over owner@, group@ or everyone@ inherit only ACEs
8804869Smarks 		 */
8817328SMark.Shellenbaum@Sun.COM 		if ((iflags & ACE_INHERIT_ONLY_ACE) &&
8827328SMark.Shellenbaum@Sun.COM 		    (entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
8837328SMark.Shellenbaum@Sun.COM 		    entry_type == OWNING_GROUP))
8844869Smarks 			continue;
8854869Smarks 
88612164SMark.Shellenbaum@Sun.COM 		if (entry_type == ACE_OWNER || (entry_type == 0 &&
88712164SMark.Shellenbaum@Sun.COM 		    who == fuid)) {
8885331Samw 			if ((access_mask & ACE_READ_DATA) &&
889789Sahrens 			    (!(seen & S_IRUSR))) {
890789Sahrens 				seen |= S_IRUSR;
8915331Samw 				if (type == ALLOW) {
892789Sahrens 					mode |= S_IRUSR;
893789Sahrens 				}
894789Sahrens 			}
8955331Samw 			if ((access_mask & ACE_WRITE_DATA) &&
896789Sahrens 			    (!(seen & S_IWUSR))) {
897789Sahrens 				seen |= S_IWUSR;
8985331Samw 				if (type == ALLOW) {
899789Sahrens 					mode |= S_IWUSR;
900789Sahrens 				}
901789Sahrens 			}
9025331Samw 			if ((access_mask & ACE_EXECUTE) &&
903789Sahrens 			    (!(seen & S_IXUSR))) {
904789Sahrens 				seen |= S_IXUSR;
9055331Samw 				if (type == ALLOW) {
906789Sahrens 					mode |= S_IXUSR;
907789Sahrens 				}
908789Sahrens 			}
90912164SMark.Shellenbaum@Sun.COM 		} else if (entry_type == OWNING_GROUP ||
91012164SMark.Shellenbaum@Sun.COM 		    (entry_type == ACE_IDENTIFIER_GROUP && who == fgid)) {
9115331Samw 			if ((access_mask & ACE_READ_DATA) &&
912789Sahrens 			    (!(seen & S_IRGRP))) {
913789Sahrens 				seen |= S_IRGRP;
9145331Samw 				if (type == ALLOW) {
915789Sahrens 					mode |= S_IRGRP;
916789Sahrens 				}
917789Sahrens 			}
9185331Samw 			if ((access_mask & ACE_WRITE_DATA) &&
919789Sahrens 			    (!(seen & S_IWGRP))) {
920789Sahrens 				seen |= S_IWGRP;
9215331Samw 				if (type == ALLOW) {
922789Sahrens 					mode |= S_IWGRP;
923789Sahrens 				}
924789Sahrens 			}
9255331Samw 			if ((access_mask & ACE_EXECUTE) &&
926789Sahrens 			    (!(seen & S_IXGRP))) {
927789Sahrens 				seen |= S_IXGRP;
9285331Samw 				if (type == ALLOW) {
929789Sahrens 					mode |= S_IXGRP;
930789Sahrens 				}
931789Sahrens 			}
932789Sahrens 		} else if (entry_type == ACE_EVERYONE) {
9335331Samw 			if ((access_mask & ACE_READ_DATA)) {
934789Sahrens 				if (!(seen & S_IRUSR)) {
935789Sahrens 					seen |= S_IRUSR;
9365331Samw 					if (type == ALLOW) {
937789Sahrens 						mode |= S_IRUSR;
938789Sahrens 					}
939789Sahrens 				}
940789Sahrens 				if (!(seen & S_IRGRP)) {
941789Sahrens 					seen |= S_IRGRP;
9425331Samw 					if (type == ALLOW) {
943789Sahrens 						mode |= S_IRGRP;
944789Sahrens 					}
945789Sahrens 				}
946789Sahrens 				if (!(seen & S_IROTH)) {
947789Sahrens 					seen |= S_IROTH;
9485331Samw 					if (type == ALLOW) {
949789Sahrens 						mode |= S_IROTH;
950789Sahrens 					}
951789Sahrens 				}
952789Sahrens 			}
9535331Samw 			if ((access_mask & ACE_WRITE_DATA)) {
954789Sahrens 				if (!(seen & S_IWUSR)) {
955789Sahrens 					seen |= S_IWUSR;
9565331Samw 					if (type == ALLOW) {
957789Sahrens 						mode |= S_IWUSR;
958789Sahrens 					}
959789Sahrens 				}
960789Sahrens 				if (!(seen & S_IWGRP)) {
961789Sahrens 					seen |= S_IWGRP;
9625331Samw 					if (type == ALLOW) {
963789Sahrens 						mode |= S_IWGRP;
964789Sahrens 					}
965789Sahrens 				}
966789Sahrens 				if (!(seen & S_IWOTH)) {
967789Sahrens 					seen |= S_IWOTH;
9685331Samw 					if (type == ALLOW) {
969789Sahrens 						mode |= S_IWOTH;
970789Sahrens 					}
971789Sahrens 				}
972789Sahrens 			}
9735331Samw 			if ((access_mask & ACE_EXECUTE)) {
974789Sahrens 				if (!(seen & S_IXUSR)) {
975789Sahrens 					seen |= S_IXUSR;
9765331Samw 					if (type == ALLOW) {
977789Sahrens 						mode |= S_IXUSR;
978789Sahrens 					}
979789Sahrens 				}
980789Sahrens 				if (!(seen & S_IXGRP)) {
981789Sahrens 					seen |= S_IXGRP;
9825331Samw 					if (type == ALLOW) {
983789Sahrens 						mode |= S_IXGRP;
984789Sahrens 					}
985789Sahrens 				}
986789Sahrens 				if (!(seen & S_IXOTH)) {
987789Sahrens 					seen |= S_IXOTH;
9885331Samw 					if (type == ALLOW) {
989789Sahrens 						mode |= S_IXOTH;
990789Sahrens 					}
991789Sahrens 				}
992789Sahrens 			}
9939981STim.Haley@Sun.COM 		} else {
9949981STim.Haley@Sun.COM 			/*
9959981STim.Haley@Sun.COM 			 * Only care if this IDENTIFIER_GROUP or
9969981STim.Haley@Sun.COM 			 * USER ACE denies execute access to someone,
9979981STim.Haley@Sun.COM 			 * mode is not affected
9989981STim.Haley@Sun.COM 			 */
9999981STim.Haley@Sun.COM 			if ((access_mask & ACE_EXECUTE) && type == DENY)
10009981STim.Haley@Sun.COM 				an_exec_denied = B_TRUE;
1001789Sahrens 		}
1002789Sahrens 	}
10039981STim.Haley@Sun.COM 
100410143STim.Haley@Sun.COM 	/*
100510143STim.Haley@Sun.COM 	 * Failure to allow is effectively a deny, so execute permission
100610143STim.Haley@Sun.COM 	 * is denied if it was never mentioned or if we explicitly
100710143STim.Haley@Sun.COM 	 * weren't allowed it.
100810143STim.Haley@Sun.COM 	 */
100910143STim.Haley@Sun.COM 	if (!an_exec_denied &&
101010143STim.Haley@Sun.COM 	    ((seen & ALL_MODE_EXECS) != ALL_MODE_EXECS ||
101110143STim.Haley@Sun.COM 	    (mode & ALL_MODE_EXECS) != ALL_MODE_EXECS))
10129981STim.Haley@Sun.COM 		an_exec_denied = B_TRUE;
10139981STim.Haley@Sun.COM 
10149981STim.Haley@Sun.COM 	if (an_exec_denied)
101511935SMark.Shellenbaum@Sun.COM 		*pflags &= ~ZFS_NO_EXECS_DENIED;
10169981STim.Haley@Sun.COM 	else
101711935SMark.Shellenbaum@Sun.COM 		*pflags |= ZFS_NO_EXECS_DENIED;
10189981STim.Haley@Sun.COM 
1019789Sahrens 	return (mode);
1020789Sahrens }
1021789Sahrens 
1022789Sahrens /*
102310143STim.Haley@Sun.COM  * Read an external acl object.  If the intent is to modify, always
102410143STim.Haley@Sun.COM  * create a new acl and leave any cached acl in place.
1025789Sahrens  */
10261544Seschrock static int
10275331Samw zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp, boolean_t will_modify)
1028789Sahrens {
1029789Sahrens 	zfs_acl_t	*aclp;
103011935SMark.Shellenbaum@Sun.COM 	int		aclsize;
103111935SMark.Shellenbaum@Sun.COM 	int		acl_count;
10325331Samw 	zfs_acl_node_t	*aclnode;
103311935SMark.Shellenbaum@Sun.COM 	zfs_acl_phys_t	znode_acl;
103411935SMark.Shellenbaum@Sun.COM 	int		version;
103511935SMark.Shellenbaum@Sun.COM 	int		error;
1036789Sahrens 
1037789Sahrens 	ASSERT(MUTEX_HELD(&zp->z_acl_lock));
1038789Sahrens 
103910143STim.Haley@Sun.COM 	if (zp->z_acl_cached && !will_modify) {
10409981STim.Haley@Sun.COM 		*aclpp = zp->z_acl_cached;
10419981STim.Haley@Sun.COM 		return (0);
10429981STim.Haley@Sun.COM 	}
10439981STim.Haley@Sun.COM 
104411935SMark.Shellenbaum@Sun.COM 	version = ZNODE_ACL_VERSION(zp);
104511935SMark.Shellenbaum@Sun.COM 
104611935SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_znode_info(zp, &aclsize,
104711935SMark.Shellenbaum@Sun.COM 	    &acl_count, &znode_acl)) != 0)
104811935SMark.Shellenbaum@Sun.COM 		return (error);
104911935SMark.Shellenbaum@Sun.COM 
105011935SMark.Shellenbaum@Sun.COM 	aclp = zfs_acl_alloc(version);
105111935SMark.Shellenbaum@Sun.COM 
10525331Samw 	aclp->z_acl_count = acl_count;
10535331Samw 	aclp->z_acl_bytes = aclsize;
10545331Samw 
105511935SMark.Shellenbaum@Sun.COM 	aclnode = zfs_acl_node_alloc(aclsize);
105611935SMark.Shellenbaum@Sun.COM 	aclnode->z_ace_count = aclp->z_acl_count;
105711935SMark.Shellenbaum@Sun.COM 	aclnode->z_size = aclsize;
105811935SMark.Shellenbaum@Sun.COM 
105911935SMark.Shellenbaum@Sun.COM 	if (!zp->z_is_sa) {
106011935SMark.Shellenbaum@Sun.COM 		if (znode_acl.z_acl_extern_obj) {
106111935SMark.Shellenbaum@Sun.COM 			error = dmu_read(zp->z_zfsvfs->z_os,
106211935SMark.Shellenbaum@Sun.COM 			    znode_acl.z_acl_extern_obj, 0, aclnode->z_size,
106311935SMark.Shellenbaum@Sun.COM 			    aclnode->z_acldata, DMU_READ_PREFETCH);
106411935SMark.Shellenbaum@Sun.COM 		} else {
106511935SMark.Shellenbaum@Sun.COM 			bcopy(znode_acl.z_ace_data, aclnode->z_acldata,
106611935SMark.Shellenbaum@Sun.COM 			    aclnode->z_size);
106711935SMark.Shellenbaum@Sun.COM 		}
106811935SMark.Shellenbaum@Sun.COM 	} else {
106911935SMark.Shellenbaum@Sun.COM 		error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DACL_ACES(zp->z_zfsvfs),
107011935SMark.Shellenbaum@Sun.COM 		    aclnode->z_acldata, aclnode->z_size);
107111935SMark.Shellenbaum@Sun.COM 	}
107211935SMark.Shellenbaum@Sun.COM 
10731544Seschrock 	if (error != 0) {
10741544Seschrock 		zfs_acl_free(aclp);
107511935SMark.Shellenbaum@Sun.COM 		zfs_acl_node_free(aclnode);
10767294Sperrin 		/* convert checksum errors into IO errors */
10777294Sperrin 		if (error == ECKSUM)
10787294Sperrin 			error = EIO;
10791544Seschrock 		return (error);
10801544Seschrock 	}
1081789Sahrens 
108211935SMark.Shellenbaum@Sun.COM 	list_insert_head(&aclp->z_acl, aclnode);
108311935SMark.Shellenbaum@Sun.COM 
108410143STim.Haley@Sun.COM 	*aclpp = aclp;
108510143STim.Haley@Sun.COM 	if (!will_modify)
108610143STim.Haley@Sun.COM 		zp->z_acl_cached = aclp;
10871544Seschrock 	return (0);
1088789Sahrens }
1089789Sahrens 
109011935SMark.Shellenbaum@Sun.COM /*ARGSUSED*/
109111935SMark.Shellenbaum@Sun.COM void
109211935SMark.Shellenbaum@Sun.COM zfs_acl_data_locator(void **dataptr, uint32_t *length, uint32_t buflen,
109311935SMark.Shellenbaum@Sun.COM     boolean_t start, void *userdata)
109411935SMark.Shellenbaum@Sun.COM {
109511935SMark.Shellenbaum@Sun.COM 	zfs_acl_locator_cb_t *cb = (zfs_acl_locator_cb_t *)userdata;
109611935SMark.Shellenbaum@Sun.COM 
109711935SMark.Shellenbaum@Sun.COM 	if (start) {
109811935SMark.Shellenbaum@Sun.COM 		cb->cb_acl_node = list_head(&cb->cb_aclp->z_acl);
109911935SMark.Shellenbaum@Sun.COM 	} else {
110011935SMark.Shellenbaum@Sun.COM 		cb->cb_acl_node = list_next(&cb->cb_aclp->z_acl,
110111935SMark.Shellenbaum@Sun.COM 		    cb->cb_acl_node);
110211935SMark.Shellenbaum@Sun.COM 	}
110311935SMark.Shellenbaum@Sun.COM 	*dataptr = cb->cb_acl_node->z_acldata;
110411935SMark.Shellenbaum@Sun.COM 	*length = cb->cb_acl_node->z_size;
110511935SMark.Shellenbaum@Sun.COM }
110611935SMark.Shellenbaum@Sun.COM 
110712164SMark.Shellenbaum@Sun.COM 
110812164SMark.Shellenbaum@Sun.COM static int
110912164SMark.Shellenbaum@Sun.COM zfs_acl_get_owner_fuids(znode_t *zp, uint64_t *fuid, uint64_t *fgid)
111012164SMark.Shellenbaum@Sun.COM {
111112164SMark.Shellenbaum@Sun.COM 	int count = 0;
111212164SMark.Shellenbaum@Sun.COM 	sa_bulk_attr_t	bulk[2];
111312164SMark.Shellenbaum@Sun.COM 	int error;
111412164SMark.Shellenbaum@Sun.COM 
111512164SMark.Shellenbaum@Sun.COM 	if (IS_EPHEMERAL(zp->z_uid) || IS_EPHEMERAL(zp->z_gid)) {
111612164SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zp->z_zfsvfs), NULL,
111712164SMark.Shellenbaum@Sun.COM 		    &fuid, sizeof (fuid));
111812164SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zp->z_zfsvfs), NULL,
111912164SMark.Shellenbaum@Sun.COM 		    &fgid, sizeof (fuid));
112012164SMark.Shellenbaum@Sun.COM 		if ((error = sa_bulk_lookup(zp->z_sa_hdl, bulk, count)) != 0) {
112112164SMark.Shellenbaum@Sun.COM 			return (error);
112212164SMark.Shellenbaum@Sun.COM 		}
112312164SMark.Shellenbaum@Sun.COM 	} else {
112412164SMark.Shellenbaum@Sun.COM 		*fuid = zp->z_uid;
112512164SMark.Shellenbaum@Sun.COM 		*fgid = zp->z_gid;
112612164SMark.Shellenbaum@Sun.COM 	}
112712164SMark.Shellenbaum@Sun.COM 	return (0);
112812164SMark.Shellenbaum@Sun.COM }
112912164SMark.Shellenbaum@Sun.COM 
113012164SMark.Shellenbaum@Sun.COM int
113112164SMark.Shellenbaum@Sun.COM zfs_acl_chown_setattr(znode_t *zp)
113212164SMark.Shellenbaum@Sun.COM {
113312164SMark.Shellenbaum@Sun.COM 	int error;
113412164SMark.Shellenbaum@Sun.COM 	zfs_acl_t *aclp;
113512164SMark.Shellenbaum@Sun.COM 	uint64_t fuid, fgid;
113612164SMark.Shellenbaum@Sun.COM 
113712164SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_get_owner_fuids(zp, &fuid, &fgid)) != 0)
113812164SMark.Shellenbaum@Sun.COM 		return (error);
113912164SMark.Shellenbaum@Sun.COM 
114012164SMark.Shellenbaum@Sun.COM 	mutex_enter(&zp->z_acl_lock);
114112164SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_node_read(zp, &aclp, B_FALSE)) == 0)
114212164SMark.Shellenbaum@Sun.COM 		zp->z_mode = zfs_mode_compute(zp->z_mode, aclp,
114312164SMark.Shellenbaum@Sun.COM 		    &zp->z_pflags, fuid, fgid);
114412164SMark.Shellenbaum@Sun.COM 	mutex_exit(&zp->z_acl_lock);
114512164SMark.Shellenbaum@Sun.COM 	return (error);
114612164SMark.Shellenbaum@Sun.COM }
114712164SMark.Shellenbaum@Sun.COM 
1148789Sahrens /*
11495331Samw  * common code for setting ACLs.
1150789Sahrens  *
1151789Sahrens  * This function is called from zfs_mode_update, zfs_perm_init, and zfs_setacl.
1152789Sahrens  * zfs_setacl passes a non-NULL inherit pointer (ihp) to indicate that it's
1153789Sahrens  * already checked the acl and knows whether to inherit.
1154789Sahrens  */
1155789Sahrens int
11569179SMark.Shellenbaum@Sun.COM zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr, dmu_tx_t *tx)
1157789Sahrens {
115811935SMark.Shellenbaum@Sun.COM 	int			error;
115911935SMark.Shellenbaum@Sun.COM 	zfsvfs_t		*zfsvfs = zp->z_zfsvfs;
116011935SMark.Shellenbaum@Sun.COM 	dmu_object_type_t	otype;
116111935SMark.Shellenbaum@Sun.COM 	zfs_acl_locator_cb_t	locate = { 0 };
116211935SMark.Shellenbaum@Sun.COM 	uint64_t		mode;
116311935SMark.Shellenbaum@Sun.COM 	sa_bulk_attr_t		bulk[5];
116411935SMark.Shellenbaum@Sun.COM 	uint64_t		ctime[2];
116511935SMark.Shellenbaum@Sun.COM 	int			count = 0;
116612164SMark.Shellenbaum@Sun.COM 	uint64_t		fuid, fgid;
116711935SMark.Shellenbaum@Sun.COM 
116811935SMark.Shellenbaum@Sun.COM 	mode = zp->z_mode;
116912164SMark.Shellenbaum@Sun.COM 
117012164SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_get_owner_fuids(zp, &fuid, &fgid)) != 0)
117112164SMark.Shellenbaum@Sun.COM 		return (error);
117212164SMark.Shellenbaum@Sun.COM 
117312164SMark.Shellenbaum@Sun.COM 	mode = zfs_mode_compute(mode, aclp, &zp->z_pflags, fuid, fgid);
117411935SMark.Shellenbaum@Sun.COM 
117511935SMark.Shellenbaum@Sun.COM 	zp->z_mode = mode;
117611935SMark.Shellenbaum@Sun.COM 	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL,
117711935SMark.Shellenbaum@Sun.COM 	    &mode, sizeof (mode));
117811935SMark.Shellenbaum@Sun.COM 	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
117911935SMark.Shellenbaum@Sun.COM 	    &zp->z_pflags, sizeof (zp->z_pflags));
118011935SMark.Shellenbaum@Sun.COM 	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
118111935SMark.Shellenbaum@Sun.COM 	    &ctime, sizeof (ctime));
1182789Sahrens 
118310143STim.Haley@Sun.COM 	if (zp->z_acl_cached) {
11849981STim.Haley@Sun.COM 		zfs_acl_free(zp->z_acl_cached);
11859981STim.Haley@Sun.COM 		zp->z_acl_cached = NULL;
11869981STim.Haley@Sun.COM 	}
11879981STim.Haley@Sun.COM 
1188789Sahrens 	/*
118911935SMark.Shellenbaum@Sun.COM 	 * Upgrade needed?
1190789Sahrens 	 */
11915331Samw 	if (!zfsvfs->z_use_fuids) {
11925331Samw 		otype = DMU_OT_OLDACL;
11935331Samw 	} else {
11945331Samw 		if ((aclp->z_version == ZFS_ACL_VERSION_INITIAL) &&
11955331Samw 		    (zfsvfs->z_version >= ZPL_VERSION_FUID))
11969179SMark.Shellenbaum@Sun.COM 			zfs_acl_xform(zp, aclp, cr);
11975331Samw 		ASSERT(aclp->z_version >= ZFS_ACL_VERSION_FUID);
11985331Samw 		otype = DMU_OT_ACL;
11995331Samw 	}
12005331Samw 
120111935SMark.Shellenbaum@Sun.COM 	/*
120211935SMark.Shellenbaum@Sun.COM 	 * Arrgh, we have to handle old on disk format
120311935SMark.Shellenbaum@Sun.COM 	 * as well as newer (preferred) SA format.
120411935SMark.Shellenbaum@Sun.COM 	 */
120511935SMark.Shellenbaum@Sun.COM 
120611935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa) { /* the easy case, just update the ACL attribute */
120711935SMark.Shellenbaum@Sun.COM 		locate.cb_aclp = aclp;
120811935SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_ACES(zfsvfs),
120911935SMark.Shellenbaum@Sun.COM 		    zfs_acl_data_locator, &locate, aclp->z_acl_bytes);
121011935SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_COUNT(zfsvfs),
121111935SMark.Shellenbaum@Sun.COM 		    NULL, &aclp->z_acl_count, sizeof (uint64_t));
121211935SMark.Shellenbaum@Sun.COM 	} else { /* Painful legacy way */
121311935SMark.Shellenbaum@Sun.COM 		zfs_acl_node_t *aclnode;
121411935SMark.Shellenbaum@Sun.COM 		uint64_t off = 0;
121511935SMark.Shellenbaum@Sun.COM 		zfs_acl_phys_t acl_phys;
121611935SMark.Shellenbaum@Sun.COM 		uint64_t aoid;
121711935SMark.Shellenbaum@Sun.COM 
121811935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs),
121911935SMark.Shellenbaum@Sun.COM 		    &acl_phys, sizeof (acl_phys))) != 0)
122011935SMark.Shellenbaum@Sun.COM 			return (error);
122111935SMark.Shellenbaum@Sun.COM 
122211935SMark.Shellenbaum@Sun.COM 		aoid = acl_phys.z_acl_extern_obj;
122311935SMark.Shellenbaum@Sun.COM 
122411935SMark.Shellenbaum@Sun.COM 		if (aclp->z_acl_bytes > ZFS_ACE_SPACE) {
122511935SMark.Shellenbaum@Sun.COM 			/*
122611935SMark.Shellenbaum@Sun.COM 			 * If ACL was previously external and we are now
122711935SMark.Shellenbaum@Sun.COM 			 * converting to new ACL format then release old
122811935SMark.Shellenbaum@Sun.COM 			 * ACL object and create a new one.
122911935SMark.Shellenbaum@Sun.COM 			 */
123011935SMark.Shellenbaum@Sun.COM 			if (aoid &&
123111935SMark.Shellenbaum@Sun.COM 			    aclp->z_version != acl_phys.z_acl_version) {
123211935SMark.Shellenbaum@Sun.COM 				error = dmu_object_free(zfsvfs->z_os, aoid, tx);
123311935SMark.Shellenbaum@Sun.COM 				if (error)
123411935SMark.Shellenbaum@Sun.COM 					return (error);
123511935SMark.Shellenbaum@Sun.COM 				aoid = 0;
123611935SMark.Shellenbaum@Sun.COM 			}
123711935SMark.Shellenbaum@Sun.COM 			if (aoid == 0) {
123811935SMark.Shellenbaum@Sun.COM 				aoid = dmu_object_alloc(zfsvfs->z_os,
123911935SMark.Shellenbaum@Sun.COM 				    otype, aclp->z_acl_bytes,
124011935SMark.Shellenbaum@Sun.COM 				    otype == DMU_OT_ACL ?
124111935SMark.Shellenbaum@Sun.COM 				    DMU_OT_SYSACL : DMU_OT_NONE,
124211935SMark.Shellenbaum@Sun.COM 				    otype == DMU_OT_ACL ?
124311935SMark.Shellenbaum@Sun.COM 				    DN_MAX_BONUSLEN : 0, tx);
124411935SMark.Shellenbaum@Sun.COM 			} else {
124511935SMark.Shellenbaum@Sun.COM 				(void) dmu_object_set_blocksize(zfsvfs->z_os,
124611935SMark.Shellenbaum@Sun.COM 				    aoid, aclp->z_acl_bytes, 0, tx);
124711935SMark.Shellenbaum@Sun.COM 			}
124811935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_extern_obj = aoid;
124911935SMark.Shellenbaum@Sun.COM 			for (aclnode = list_head(&aclp->z_acl); aclnode;
125011935SMark.Shellenbaum@Sun.COM 			    aclnode = list_next(&aclp->z_acl, aclnode)) {
125111935SMark.Shellenbaum@Sun.COM 				if (aclnode->z_ace_count == 0)
125211935SMark.Shellenbaum@Sun.COM 					continue;
125311935SMark.Shellenbaum@Sun.COM 				dmu_write(zfsvfs->z_os, aoid, off,
125411935SMark.Shellenbaum@Sun.COM 				    aclnode->z_size, aclnode->z_acldata, tx);
125511935SMark.Shellenbaum@Sun.COM 				off += aclnode->z_size;
125611935SMark.Shellenbaum@Sun.COM 			}
1257789Sahrens 		} else {
125811935SMark.Shellenbaum@Sun.COM 			void *start = acl_phys.z_ace_data;
125911935SMark.Shellenbaum@Sun.COM 			/*
126011935SMark.Shellenbaum@Sun.COM 			 * Migrating back embedded?
126111935SMark.Shellenbaum@Sun.COM 			 */
126211935SMark.Shellenbaum@Sun.COM 			if (acl_phys.z_acl_extern_obj) {
126311935SMark.Shellenbaum@Sun.COM 				error = dmu_object_free(zfsvfs->z_os,
126411935SMark.Shellenbaum@Sun.COM 				    acl_phys.z_acl_extern_obj, tx);
126511935SMark.Shellenbaum@Sun.COM 				if (error)
126611935SMark.Shellenbaum@Sun.COM 					return (error);
126711935SMark.Shellenbaum@Sun.COM 				acl_phys.z_acl_extern_obj = 0;
126811935SMark.Shellenbaum@Sun.COM 			}
126911935SMark.Shellenbaum@Sun.COM 
127011935SMark.Shellenbaum@Sun.COM 			for (aclnode = list_head(&aclp->z_acl); aclnode;
127111935SMark.Shellenbaum@Sun.COM 			    aclnode = list_next(&aclp->z_acl, aclnode)) {
127211935SMark.Shellenbaum@Sun.COM 				if (aclnode->z_ace_count == 0)
127311935SMark.Shellenbaum@Sun.COM 					continue;
127411935SMark.Shellenbaum@Sun.COM 				bcopy(aclnode->z_acldata, start,
127511935SMark.Shellenbaum@Sun.COM 				    aclnode->z_size);
127611935SMark.Shellenbaum@Sun.COM 				start = (caddr_t)start + aclnode->z_size;
127711935SMark.Shellenbaum@Sun.COM 			}
12785331Samw 		}
1279789Sahrens 		/*
128011935SMark.Shellenbaum@Sun.COM 		 * If Old version then swap count/bytes to match old
128111935SMark.Shellenbaum@Sun.COM 		 * layout of znode_acl_phys_t.
1282789Sahrens 		 */
128311935SMark.Shellenbaum@Sun.COM 		if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) {
128411935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_size = aclp->z_acl_count;
128511935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_count = aclp->z_acl_bytes;
128611935SMark.Shellenbaum@Sun.COM 		} else {
128711935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_size = aclp->z_acl_bytes;
128811935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_count = aclp->z_acl_count;
1289789Sahrens 		}
129011935SMark.Shellenbaum@Sun.COM 		acl_phys.z_acl_version = aclp->z_version;
129111935SMark.Shellenbaum@Sun.COM 
129211935SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ZNODE_ACL(zfsvfs), NULL,
129311935SMark.Shellenbaum@Sun.COM 		    &acl_phys, sizeof (acl_phys));
1294789Sahrens 	}
1295905Smarks 
12965331Samw 	/*
12975331Samw 	 * Replace ACL wide bits, but first clear them.
12985331Samw 	 */
129911935SMark.Shellenbaum@Sun.COM 	zp->z_pflags &= ~ZFS_ACL_WIDE_FLAGS;
130011935SMark.Shellenbaum@Sun.COM 
130111935SMark.Shellenbaum@Sun.COM 	zp->z_pflags |= aclp->z_hints;
13025331Samw 
13035331Samw 	if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0)
130411935SMark.Shellenbaum@Sun.COM 		zp->z_pflags |= ZFS_ACL_TRIVIAL;
130511935SMark.Shellenbaum@Sun.COM 
130611935SMark.Shellenbaum@Sun.COM 	zfs_tstamp_update_setup(zp, STATE_CHANGED, NULL, ctime, B_TRUE);
130711935SMark.Shellenbaum@Sun.COM 	return (sa_bulk_update(zp->z_sa_hdl, bulk, count, tx));
1308789Sahrens }
1309789Sahrens 
1310789Sahrens /*
1311789Sahrens  * Update access mask for prepended ACE
1312789Sahrens  *
1313789Sahrens  * This applies the "groupmask" value for aclmode property.
1314789Sahrens  */
1315789Sahrens static void
13165331Samw zfs_acl_prepend_fixup(zfs_acl_t *aclp, void  *acep, void  *origacep,
13175331Samw     mode_t mode, uint64_t owner)
1318789Sahrens {
1319789Sahrens 	int	rmask, wmask, xmask;
1320789Sahrens 	int	user_ace;
13215331Samw 	uint16_t aceflags;
13225331Samw 	uint32_t origmask, acepmask;
13235331Samw 	uint64_t fuid;
1324789Sahrens 
13255331Samw 	aceflags = aclp->z_ops.ace_flags_get(acep);
13265331Samw 	fuid = aclp->z_ops.ace_who_get(acep);
13275331Samw 	origmask = aclp->z_ops.ace_mask_get(origacep);
13285331Samw 	acepmask = aclp->z_ops.ace_mask_get(acep);
13295331Samw 
13305331Samw 	user_ace = (!(aceflags &
1331789Sahrens 	    (ACE_OWNER|ACE_GROUP|ACE_IDENTIFIER_GROUP)));
1332789Sahrens 
13335331Samw 	if (user_ace && (fuid == owner)) {
1334789Sahrens 		rmask = S_IRUSR;
1335789Sahrens 		wmask = S_IWUSR;
1336789Sahrens 		xmask = S_IXUSR;
1337789Sahrens 	} else {
1338789Sahrens 		rmask = S_IRGRP;
1339789Sahrens 		wmask = S_IWGRP;
1340789Sahrens 		xmask = S_IXGRP;
1341789Sahrens 	}
1342789Sahrens 
13435331Samw 	if (origmask & ACE_READ_DATA) {
13445331Samw 		if (mode & rmask) {
13455331Samw 			acepmask &= ~ACE_READ_DATA;
13465331Samw 		} else {
13475331Samw 			acepmask |= ACE_READ_DATA;
13485331Samw 		}
1349789Sahrens 	}
1350789Sahrens 
13515331Samw 	if (origmask & ACE_WRITE_DATA) {
13525331Samw 		if (mode & wmask) {
13535331Samw 			acepmask &= ~ACE_WRITE_DATA;
13545331Samw 		} else {
13555331Samw 			acepmask |= ACE_WRITE_DATA;
13565331Samw 		}
1357789Sahrens 	}
1358789Sahrens 
13595331Samw 	if (origmask & ACE_APPEND_DATA) {
13605331Samw 		if (mode & wmask) {
13615331Samw 			acepmask &= ~ACE_APPEND_DATA;
13625331Samw 		} else {
13635331Samw 			acepmask |= ACE_APPEND_DATA;
13645331Samw 		}
1365789Sahrens 	}
1366789Sahrens 
13675331Samw 	if (origmask & ACE_EXECUTE) {
13685331Samw 		if (mode & xmask) {
13695331Samw 			acepmask &= ~ACE_EXECUTE;
13705331Samw 		} else {
13715331Samw 			acepmask |= ACE_EXECUTE;
13725331Samw 		}
1373789Sahrens 	}
13745331Samw 	aclp->z_ops.ace_mask_set(acep, acepmask);
1375789Sahrens }
1376789Sahrens 
1377789Sahrens static void
137812164SMark.Shellenbaum@Sun.COM zfs_acl_chmod(zfsvfs_t *zfsvfs, uint64_t mode, zfs_acl_t *aclp)
1379789Sahrens {
138012164SMark.Shellenbaum@Sun.COM 	void		*acep = NULL;
13815331Samw 	uint64_t	who;
138212164SMark.Shellenbaum@Sun.COM 	int		new_count, new_bytes;
138312164SMark.Shellenbaum@Sun.COM 	int		ace_size;
1384789Sahrens 	int 		entry_type;
13855331Samw 	uint16_t	iflags, type;
13865331Samw 	uint32_t	access_mask;
138712164SMark.Shellenbaum@Sun.COM 	zfs_acl_node_t	*newnode;
138812164SMark.Shellenbaum@Sun.COM 	size_t 		abstract_size = aclp->z_ops.ace_abstract_size();
138912164SMark.Shellenbaum@Sun.COM 	void 		*zacep;
139012164SMark.Shellenbaum@Sun.COM 	uint32_t 	owner, group, everyone;
139112164SMark.Shellenbaum@Sun.COM 	uint32_t	deny1, deny2, allow0;
1392789Sahrens 
139312164SMark.Shellenbaum@Sun.COM 	new_count = new_bytes = 0;
139412164SMark.Shellenbaum@Sun.COM 
139512164SMark.Shellenbaum@Sun.COM 	acl_trivial_access_masks((mode_t)mode, &allow0, &deny1, &deny2,
139612164SMark.Shellenbaum@Sun.COM 	    &owner, &group, &everyone);
139712164SMark.Shellenbaum@Sun.COM 
139812164SMark.Shellenbaum@Sun.COM 	newnode = zfs_acl_node_alloc((abstract_size * 6) + aclp->z_acl_bytes);
139912164SMark.Shellenbaum@Sun.COM 
140012164SMark.Shellenbaum@Sun.COM 	zacep = newnode->z_acldata;
140112164SMark.Shellenbaum@Sun.COM 	if (allow0) {
140212164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, allow0, ALLOW, -1, ACE_OWNER);
140312164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + abstract_size);
140412164SMark.Shellenbaum@Sun.COM 		new_count++;
140512164SMark.Shellenbaum@Sun.COM 		new_bytes += abstract_size;
140612164SMark.Shellenbaum@Sun.COM 	} if (deny1) {
140712164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, deny1, DENY, -1, ACE_OWNER);
140812164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + abstract_size);
140912164SMark.Shellenbaum@Sun.COM 		new_count++;
141012164SMark.Shellenbaum@Sun.COM 		new_bytes += abstract_size;
141112164SMark.Shellenbaum@Sun.COM 	}
141212164SMark.Shellenbaum@Sun.COM 	if (deny2) {
141312164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, deny2, DENY, -1, OWNING_GROUP);
141412164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + abstract_size);
141512164SMark.Shellenbaum@Sun.COM 		new_count++;
141612164SMark.Shellenbaum@Sun.COM 		new_bytes += abstract_size;
141712164SMark.Shellenbaum@Sun.COM 	}
14185489Smarks 
14195331Samw 	while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
14205331Samw 	    &iflags, &type)) {
142112164SMark.Shellenbaum@Sun.COM 		uint16_t inherit_flags;
14225331Samw 
14235331Samw 		entry_type = (iflags & ACE_TYPE_FLAGS);
142412164SMark.Shellenbaum@Sun.COM 		inherit_flags = (iflags & ALL_INHERIT);
142512164SMark.Shellenbaum@Sun.COM 
142612164SMark.Shellenbaum@Sun.COM 		if ((entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
142712164SMark.Shellenbaum@Sun.COM 		    (entry_type == OWNING_GROUP)) &&
142812164SMark.Shellenbaum@Sun.COM 		    ((inherit_flags & ACE_INHERIT_ONLY_ACE) == 0)) {
142912164SMark.Shellenbaum@Sun.COM 			continue;
143012164SMark.Shellenbaum@Sun.COM 		}
1431789Sahrens 
14325331Samw 		if ((type != ALLOW && type != DENY) ||
143312164SMark.Shellenbaum@Sun.COM 		    (inherit_flags & ACE_INHERIT_ONLY_ACE)) {
143412164SMark.Shellenbaum@Sun.COM 			if (inherit_flags)
14355331Samw 				aclp->z_hints |= ZFS_INHERIT_ACE;
14365331Samw 			switch (type) {
14375331Samw 			case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
14385331Samw 			case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
14395331Samw 			case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
14405331Samw 			case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
14415331Samw 				aclp->z_hints |= ZFS_ACL_OBJ_ACE;
14425331Samw 				break;
14435331Samw 			}
1444789Sahrens 		} else {
1445789Sahrens 
144612164SMark.Shellenbaum@Sun.COM 			/*
144712164SMark.Shellenbaum@Sun.COM 			 * Limit permissions to be no greater than
144812164SMark.Shellenbaum@Sun.COM 			 * group permissions
144912164SMark.Shellenbaum@Sun.COM 			 */
145012164SMark.Shellenbaum@Sun.COM 			if (zfsvfs->z_acl_inherit == ZFS_ACL_RESTRICTED) {
145112164SMark.Shellenbaum@Sun.COM 				if (!(mode & S_IRGRP))
145212164SMark.Shellenbaum@Sun.COM 					access_mask &= ~ACE_READ_DATA;
145312164SMark.Shellenbaum@Sun.COM 				if (!(mode & S_IWGRP))
145412164SMark.Shellenbaum@Sun.COM 					access_mask &=
145512164SMark.Shellenbaum@Sun.COM 					    ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
145612164SMark.Shellenbaum@Sun.COM 				if (!(mode & S_IXGRP))
145712164SMark.Shellenbaum@Sun.COM 					access_mask &= ~ACE_EXECUTE;
145812164SMark.Shellenbaum@Sun.COM 				access_mask &=
145912164SMark.Shellenbaum@Sun.COM 				    ~(ACE_WRITE_OWNER|ACE_WRITE_ACL|
146012164SMark.Shellenbaum@Sun.COM 				    ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS);
1461789Sahrens 			}
1462789Sahrens 		}
146312164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, access_mask, type, who, iflags);
146412164SMark.Shellenbaum@Sun.COM 		ace_size = aclp->z_ops.ace_size(acep);
146512164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + ace_size);
146612164SMark.Shellenbaum@Sun.COM 		new_count++;
146712164SMark.Shellenbaum@Sun.COM 		new_bytes += ace_size;
1468789Sahrens 	}
146912164SMark.Shellenbaum@Sun.COM 	zfs_set_ace(aclp, zacep, owner, 0, -1, ACE_OWNER);
147012164SMark.Shellenbaum@Sun.COM 	zacep = (void *)((uintptr_t)zacep + abstract_size);
147112164SMark.Shellenbaum@Sun.COM 	zfs_set_ace(aclp, zacep, group, 0, -1, OWNING_GROUP);
147212164SMark.Shellenbaum@Sun.COM 	zacep = (void *)((uintptr_t)zacep + abstract_size);
147312164SMark.Shellenbaum@Sun.COM 	zfs_set_ace(aclp, zacep, everyone, 0, -1, ACE_EVERYONE);
1474789Sahrens 
147512164SMark.Shellenbaum@Sun.COM 	new_count += 3;
147612164SMark.Shellenbaum@Sun.COM 	new_bytes += abstract_size * 3;
147712164SMark.Shellenbaum@Sun.COM 	zfs_acl_release_nodes(aclp);
147812164SMark.Shellenbaum@Sun.COM 	aclp->z_acl_count = new_count;
147912164SMark.Shellenbaum@Sun.COM 	aclp->z_acl_bytes = new_bytes;
148012164SMark.Shellenbaum@Sun.COM 	newnode->z_ace_count = new_count;
148112164SMark.Shellenbaum@Sun.COM 	newnode->z_size = new_bytes;
148212164SMark.Shellenbaum@Sun.COM 	list_insert_tail(&aclp->z_acl, newnode);
1483789Sahrens }
1484789Sahrens 
1485789Sahrens int
14865824Smarks zfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode)
1487789Sahrens {
14885824Smarks 	mutex_enter(&zp->z_lock);
1489789Sahrens 	mutex_enter(&zp->z_acl_lock);
149012164SMark.Shellenbaum@Sun.COM 	*aclp = zfs_acl_alloc(zfs_acl_version_zp(zp));
149112164SMark.Shellenbaum@Sun.COM 	(*aclp)->z_hints = zp->z_pflags & V4_ACL_WIDE_FLAGS;
149212164SMark.Shellenbaum@Sun.COM 	zfs_acl_chmod(zp->z_zfsvfs, mode, *aclp);
1493789Sahrens 	mutex_exit(&zp->z_acl_lock);
14945824Smarks 	mutex_exit(&zp->z_lock);
149512164SMark.Shellenbaum@Sun.COM 	ASSERT(*aclp);
149612164SMark.Shellenbaum@Sun.COM 	return (0);
1497789Sahrens }
1498789Sahrens 
1499789Sahrens /*
1500789Sahrens  * strip off write_owner and write_acl
1501789Sahrens  */
1502789Sahrens static void
15036385Smarks zfs_restricted_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep)
1504789Sahrens {
15055331Samw 	uint32_t mask = aclp->z_ops.ace_mask_get(acep);
15065331Samw 
15076385Smarks 	if ((zfsvfs->z_acl_inherit == ZFS_ACL_RESTRICTED) &&
15085331Samw 	    (aclp->z_ops.ace_type_get(acep) == ALLOW)) {
15096385Smarks 		mask &= ~RESTRICTED_CLEAR;
15105331Samw 		aclp->z_ops.ace_mask_set(acep, mask);
15115331Samw 	}
15125331Samw }
15135331Samw 
15145331Samw /*
15155331Samw  * Should ACE be inherited?
15165331Samw  */
15175331Samw static int
15189179SMark.Shellenbaum@Sun.COM zfs_ace_can_use(vtype_t vtype, uint16_t acep_flags)
15195331Samw {
15205331Samw 	int	iflags = (acep_flags & 0xf);
15215331Samw 
15225331Samw 	if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE))
15235331Samw 		return (1);
15245331Samw 	else if (iflags & ACE_FILE_INHERIT_ACE)
15255331Samw 		return (!((vtype == VDIR) &&
15265331Samw 		    (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)));
15275331Samw 	return (0);
1528789Sahrens }
1529789Sahrens 
1530789Sahrens /*
1531789Sahrens  * inherit inheritable ACEs from parent
1532789Sahrens  */
1533789Sahrens static zfs_acl_t *
15349179SMark.Shellenbaum@Sun.COM zfs_acl_inherit(zfsvfs_t *zfsvfs, vtype_t vtype, zfs_acl_t *paclp,
15359179SMark.Shellenbaum@Sun.COM     uint64_t mode, boolean_t *need_chmod)
1536789Sahrens {
15375331Samw 	void		*pacep;
153812164SMark.Shellenbaum@Sun.COM 	void		*acep;
153912164SMark.Shellenbaum@Sun.COM 	zfs_acl_node_t  *aclnode;
1540789Sahrens 	zfs_acl_t	*aclp = NULL;
15415331Samw 	uint64_t	who;
15425331Samw 	uint32_t	access_mask;
15435331Samw 	uint16_t	iflags, newflags, type;
15445331Samw 	size_t		ace_size;
15455331Samw 	void		*data1, *data2;
15465331Samw 	size_t		data1sz, data2sz;
15479179SMark.Shellenbaum@Sun.COM 	boolean_t	vdir = vtype == VDIR;
15489179SMark.Shellenbaum@Sun.COM 	boolean_t	vreg = vtype == VREG;
15498053SMark.Shellenbaum@Sun.COM 	boolean_t	passthrough, passthrough_x, noallow;
15508053SMark.Shellenbaum@Sun.COM 
15518053SMark.Shellenbaum@Sun.COM 	passthrough_x =
15528053SMark.Shellenbaum@Sun.COM 	    zfsvfs->z_acl_inherit == ZFS_ACL_PASSTHROUGH_X;
15538053SMark.Shellenbaum@Sun.COM 	passthrough = passthrough_x ||
15548053SMark.Shellenbaum@Sun.COM 	    zfsvfs->z_acl_inherit == ZFS_ACL_PASSTHROUGH;
15558053SMark.Shellenbaum@Sun.COM 	noallow =
15568053SMark.Shellenbaum@Sun.COM 	    zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW;
1557789Sahrens 
15586385Smarks 	*need_chmod = B_TRUE;
15595331Samw 	pacep = NULL;
15607559SMark.Shellenbaum@Sun.COM 	aclp = zfs_acl_alloc(paclp->z_version);
156111935SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_acl_inherit == ZFS_ACL_DISCARD || vtype == VLNK)
15628053SMark.Shellenbaum@Sun.COM 		return (aclp);
15638053SMark.Shellenbaum@Sun.COM 	while (pacep = zfs_acl_next_ace(paclp, pacep, &who,
15648053SMark.Shellenbaum@Sun.COM 	    &access_mask, &iflags, &type)) {
15658053SMark.Shellenbaum@Sun.COM 
15668053SMark.Shellenbaum@Sun.COM 		/*
15678053SMark.Shellenbaum@Sun.COM 		 * don't inherit bogus ACEs
15688053SMark.Shellenbaum@Sun.COM 		 */
15698053SMark.Shellenbaum@Sun.COM 		if (!zfs_acl_valid_ace_type(type, iflags))
15708053SMark.Shellenbaum@Sun.COM 			continue;
15718053SMark.Shellenbaum@Sun.COM 
15728053SMark.Shellenbaum@Sun.COM 		if (noallow && type == ALLOW)
15738053SMark.Shellenbaum@Sun.COM 			continue;
15748053SMark.Shellenbaum@Sun.COM 
15758053SMark.Shellenbaum@Sun.COM 		ace_size = aclp->z_ops.ace_size(pacep);
15768053SMark.Shellenbaum@Sun.COM 
15779179SMark.Shellenbaum@Sun.COM 		if (!zfs_ace_can_use(vtype, iflags))
15788053SMark.Shellenbaum@Sun.COM 			continue;
1579789Sahrens 
15808053SMark.Shellenbaum@Sun.COM 		/*
15818053SMark.Shellenbaum@Sun.COM 		 * If owner@, group@, or everyone@ inheritable
15828053SMark.Shellenbaum@Sun.COM 		 * then zfs_acl_chmod() isn't needed.
15838053SMark.Shellenbaum@Sun.COM 		 */
15848053SMark.Shellenbaum@Sun.COM 		if (passthrough &&
15858053SMark.Shellenbaum@Sun.COM 		    ((iflags & (ACE_OWNER|ACE_EVERYONE)) ||
15868053SMark.Shellenbaum@Sun.COM 		    ((iflags & OWNING_GROUP) ==
15878053SMark.Shellenbaum@Sun.COM 		    OWNING_GROUP)) && (vreg || (vdir && (iflags &
15888053SMark.Shellenbaum@Sun.COM 		    ACE_DIRECTORY_INHERIT_ACE)))) {
15898053SMark.Shellenbaum@Sun.COM 			*need_chmod = B_FALSE;
159011969SMark.Shellenbaum@Sun.COM 		}
159111969SMark.Shellenbaum@Sun.COM 
159211969SMark.Shellenbaum@Sun.COM 		if (!vdir && passthrough_x &&
159311969SMark.Shellenbaum@Sun.COM 		    ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)) {
159411969SMark.Shellenbaum@Sun.COM 			access_mask &= ~ACE_EXECUTE;
15958053SMark.Shellenbaum@Sun.COM 		}
1596789Sahrens 
15978053SMark.Shellenbaum@Sun.COM 		aclnode = zfs_acl_node_alloc(ace_size);
15988053SMark.Shellenbaum@Sun.COM 		list_insert_tail(&aclp->z_acl, aclnode);
15998053SMark.Shellenbaum@Sun.COM 		acep = aclnode->z_acldata;
16006385Smarks 
16018053SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, acep, access_mask, type,
16028053SMark.Shellenbaum@Sun.COM 		    who, iflags|ACE_INHERITED_ACE);
16038053SMark.Shellenbaum@Sun.COM 
16048053SMark.Shellenbaum@Sun.COM 		/*
16058053SMark.Shellenbaum@Sun.COM 		 * Copy special opaque data if any
16068053SMark.Shellenbaum@Sun.COM 		 */
16078053SMark.Shellenbaum@Sun.COM 		if ((data1sz = paclp->z_ops.ace_data(pacep, &data1)) != 0) {
16088053SMark.Shellenbaum@Sun.COM 			VERIFY((data2sz = aclp->z_ops.ace_data(acep,
16098053SMark.Shellenbaum@Sun.COM 			    &data2)) == data1sz);
16108053SMark.Shellenbaum@Sun.COM 			bcopy(data1, data2, data2sz);
16118053SMark.Shellenbaum@Sun.COM 		}
161212164SMark.Shellenbaum@Sun.COM 
16138053SMark.Shellenbaum@Sun.COM 		aclp->z_acl_count++;
16148053SMark.Shellenbaum@Sun.COM 		aclnode->z_ace_count++;
16158053SMark.Shellenbaum@Sun.COM 		aclp->z_acl_bytes += aclnode->z_size;
16168053SMark.Shellenbaum@Sun.COM 		newflags = aclp->z_ops.ace_flags_get(acep);
16178053SMark.Shellenbaum@Sun.COM 
16188053SMark.Shellenbaum@Sun.COM 		if (vdir)
16198053SMark.Shellenbaum@Sun.COM 			aclp->z_hints |= ZFS_INHERIT_ACE;
16206385Smarks 
16218053SMark.Shellenbaum@Sun.COM 		if ((iflags & ACE_NO_PROPAGATE_INHERIT_ACE) || !vdir) {
16228053SMark.Shellenbaum@Sun.COM 			newflags &= ~ALL_INHERIT;
16238053SMark.Shellenbaum@Sun.COM 			aclp->z_ops.ace_flags_set(acep,
16248053SMark.Shellenbaum@Sun.COM 			    newflags|ACE_INHERITED_ACE);
16258053SMark.Shellenbaum@Sun.COM 			zfs_restricted_update(zfsvfs, aclp, acep);
16268053SMark.Shellenbaum@Sun.COM 			continue;
16278053SMark.Shellenbaum@Sun.COM 		}
16288053SMark.Shellenbaum@Sun.COM 
16298053SMark.Shellenbaum@Sun.COM 		ASSERT(vdir);
16308053SMark.Shellenbaum@Sun.COM 
163112164SMark.Shellenbaum@Sun.COM 		/*
163212164SMark.Shellenbaum@Sun.COM 		 * If only FILE_INHERIT is set then turn on
163312164SMark.Shellenbaum@Sun.COM 		 * inherit_only
163412164SMark.Shellenbaum@Sun.COM 		 */
16358053SMark.Shellenbaum@Sun.COM 		if ((iflags & (ACE_FILE_INHERIT_ACE |
1636*12322SMark.Shellenbaum@Sun.COM 		    ACE_DIRECTORY_INHERIT_ACE)) == ACE_FILE_INHERIT_ACE) {
16378053SMark.Shellenbaum@Sun.COM 			newflags |= ACE_INHERIT_ONLY_ACE;
16388053SMark.Shellenbaum@Sun.COM 			aclp->z_ops.ace_flags_set(acep,
16398053SMark.Shellenbaum@Sun.COM 			    newflags|ACE_INHERITED_ACE);
1640*12322SMark.Shellenbaum@Sun.COM 		} else {
1641*12322SMark.Shellenbaum@Sun.COM 			newflags &= ~ACE_INHERIT_ONLY_ACE;
1642*12322SMark.Shellenbaum@Sun.COM 			aclp->z_ops.ace_flags_set(acep,
1643*12322SMark.Shellenbaum@Sun.COM 			    newflags|ACE_INHERITED_ACE);
1644789Sahrens 		}
1645789Sahrens 	}
1646789Sahrens 	return (aclp);
1647789Sahrens }
1648789Sahrens 
1649789Sahrens /*
1650789Sahrens  * Create file system object initial permissions
1651789Sahrens  * including inheritable ACEs.
1652789Sahrens  */
16539179SMark.Shellenbaum@Sun.COM int
16549179SMark.Shellenbaum@Sun.COM zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
16559179SMark.Shellenbaum@Sun.COM     vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids)
1656789Sahrens {
1657789Sahrens 	int		error;
16589179SMark.Shellenbaum@Sun.COM 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
16595331Samw 	zfs_acl_t	*paclp;
16605959Smarks 	gid_t		gid;
16616385Smarks 	boolean_t	need_chmod = B_TRUE;
166211935SMark.Shellenbaum@Sun.COM 	boolean_t	inherited = B_FALSE;
166311935SMark.Shellenbaum@Sun.COM 	uint64_t	parentgid;
16645331Samw 
16659179SMark.Shellenbaum@Sun.COM 	bzero(acl_ids, sizeof (zfs_acl_ids_t));
16669179SMark.Shellenbaum@Sun.COM 	acl_ids->z_mode = MAKEIMODE(vap->va_type, vap->va_mode);
1667789Sahrens 
16689179SMark.Shellenbaum@Sun.COM 	if (vsecp)
16699179SMark.Shellenbaum@Sun.COM 		if ((error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, cr,
16709179SMark.Shellenbaum@Sun.COM 		    &acl_ids->z_fuidp, &acl_ids->z_aclp)) != 0)
16719179SMark.Shellenbaum@Sun.COM 			return (error);
1672789Sahrens 	/*
1673789Sahrens 	 * Determine uid and gid.
1674789Sahrens 	 */
167511249SMark.Shellenbaum@Sun.COM 	if ((flag & IS_ROOT_NODE) || zfsvfs->z_replay ||
1676789Sahrens 	    ((flag & IS_XATTR) && (vap->va_type == VDIR))) {
16779179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fuid = zfs_fuid_create(zfsvfs,
16789179SMark.Shellenbaum@Sun.COM 		    (uint64_t)vap->va_uid, cr,
16799179SMark.Shellenbaum@Sun.COM 		    ZFS_OWNER, &acl_ids->z_fuidp);
16809179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fgid = zfs_fuid_create(zfsvfs,
16819179SMark.Shellenbaum@Sun.COM 		    (uint64_t)vap->va_gid, cr,
16829179SMark.Shellenbaum@Sun.COM 		    ZFS_GROUP, &acl_ids->z_fuidp);
16835959Smarks 		gid = vap->va_gid;
1684789Sahrens 	} else {
168511935SMark.Shellenbaum@Sun.COM 		if (IS_EPHEMERAL(dzp->z_gid))
168611935SMark.Shellenbaum@Sun.COM 			VERIFY(0 == sa_lookup(dzp->z_sa_hdl, SA_ZPL_GID(zfsvfs),
168711935SMark.Shellenbaum@Sun.COM 			    &parentgid, sizeof (parentgid)));
168811935SMark.Shellenbaum@Sun.COM 		else
168911935SMark.Shellenbaum@Sun.COM 			parentgid = (uint64_t)dzp->z_gid;
169011935SMark.Shellenbaum@Sun.COM 
16919179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER,
16929179SMark.Shellenbaum@Sun.COM 		    cr, &acl_ids->z_fuidp);
16939179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fgid = 0;
16945959Smarks 		if (vap->va_mask & AT_GID)  {
16959179SMark.Shellenbaum@Sun.COM 			acl_ids->z_fgid = zfs_fuid_create(zfsvfs,
16969179SMark.Shellenbaum@Sun.COM 			    (uint64_t)vap->va_gid,
16979179SMark.Shellenbaum@Sun.COM 			    cr, ZFS_GROUP, &acl_ids->z_fuidp);
16985959Smarks 			gid = vap->va_gid;
169911935SMark.Shellenbaum@Sun.COM 			if (acl_ids->z_fgid != parentgid &&
17005959Smarks 			    !groupmember(vap->va_gid, cr) &&
17015959Smarks 			    secpolicy_vnode_create_gid(cr) != 0)
17029179SMark.Shellenbaum@Sun.COM 				acl_ids->z_fgid = 0;
17035959Smarks 		}
17049179SMark.Shellenbaum@Sun.COM 		if (acl_ids->z_fgid == 0) {
170511935SMark.Shellenbaum@Sun.COM 			if (dzp->z_mode & S_ISGID) {
170611574SJohn.Harres@Sun.COM 				char		*domain;
170711574SJohn.Harres@Sun.COM 				uint32_t	rid;
170811574SJohn.Harres@Sun.COM 
170911935SMark.Shellenbaum@Sun.COM 				acl_ids->z_fgid = parentgid;
17109179SMark.Shellenbaum@Sun.COM 				gid = zfs_fuid_map_id(zfsvfs, acl_ids->z_fgid,
17115959Smarks 				    cr, ZFS_GROUP);
171211574SJohn.Harres@Sun.COM 
171311574SJohn.Harres@Sun.COM 				if (zfsvfs->z_use_fuids &&
171411574SJohn.Harres@Sun.COM 				    IS_EPHEMERAL(acl_ids->z_fgid)) {
171511574SJohn.Harres@Sun.COM 					domain = zfs_fuid_idx_domain(
171611574SJohn.Harres@Sun.COM 					    &zfsvfs->z_fuid_idx,
171711574SJohn.Harres@Sun.COM 					    FUID_INDEX(acl_ids->z_fgid));
171811574SJohn.Harres@Sun.COM 					rid = FUID_RID(acl_ids->z_fgid);
171911574SJohn.Harres@Sun.COM 					zfs_fuid_node_add(&acl_ids->z_fuidp,
172011574SJohn.Harres@Sun.COM 					    domain, rid,
172111574SJohn.Harres@Sun.COM 					    FUID_INDEX(acl_ids->z_fgid),
172211574SJohn.Harres@Sun.COM 					    acl_ids->z_fgid, ZFS_GROUP);
172311574SJohn.Harres@Sun.COM 				}
17245959Smarks 			} else {
17259179SMark.Shellenbaum@Sun.COM 				acl_ids->z_fgid = zfs_fuid_create_cred(zfsvfs,
17269179SMark.Shellenbaum@Sun.COM 				    ZFS_GROUP, cr, &acl_ids->z_fuidp);
17275959Smarks 				gid = crgetgid(cr);
17285959Smarks 			}
17295331Samw 		}
1730789Sahrens 	}
1731789Sahrens 
1732789Sahrens 	/*
1733789Sahrens 	 * If we're creating a directory, and the parent directory has the
1734789Sahrens 	 * set-GID bit set, set in on the new directory.
1735789Sahrens 	 * Otherwise, if the user is neither privileged nor a member of the
1736789Sahrens 	 * file's new group, clear the file's set-GID bit.
1737789Sahrens 	 */
1738789Sahrens 
173911935SMark.Shellenbaum@Sun.COM 	if (!(flag & IS_ROOT_NODE) && (dzp->z_mode & S_ISGID) &&
17409179SMark.Shellenbaum@Sun.COM 	    (vap->va_type == VDIR)) {
17419179SMark.Shellenbaum@Sun.COM 		acl_ids->z_mode |= S_ISGID;
17425959Smarks 	} else {
17439179SMark.Shellenbaum@Sun.COM 		if ((acl_ids->z_mode & S_ISGID) &&
1744789Sahrens 		    secpolicy_vnode_setids_setgids(cr, gid) != 0)
17459179SMark.Shellenbaum@Sun.COM 			acl_ids->z_mode &= ~S_ISGID;
1746789Sahrens 	}
1747789Sahrens 
17489179SMark.Shellenbaum@Sun.COM 	if (acl_ids->z_aclp == NULL) {
17499179SMark.Shellenbaum@Sun.COM 		mutex_enter(&dzp->z_lock);
17509179SMark.Shellenbaum@Sun.COM 		if (!(flag & IS_ROOT_NODE) && (ZTOV(dzp)->v_type == VDIR &&
175111935SMark.Shellenbaum@Sun.COM 		    (dzp->z_pflags & ZFS_INHERIT_ACE)) &&
175211935SMark.Shellenbaum@Sun.COM 		    !(dzp->z_pflags & ZFS_XATTR)) {
17539179SMark.Shellenbaum@Sun.COM 			mutex_enter(&dzp->z_acl_lock);
17549179SMark.Shellenbaum@Sun.COM 			VERIFY(0 == zfs_acl_node_read(dzp, &paclp, B_FALSE));
17559179SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp = zfs_acl_inherit(zfsvfs,
17569179SMark.Shellenbaum@Sun.COM 			    vap->va_type, paclp, acl_ids->z_mode, &need_chmod);
175711935SMark.Shellenbaum@Sun.COM 			mutex_exit(&dzp->z_acl_lock);
175811935SMark.Shellenbaum@Sun.COM 			inherited = B_TRUE;
17595331Samw 		} else {
17609179SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp =
17619179SMark.Shellenbaum@Sun.COM 			    zfs_acl_alloc(zfs_acl_version_zp(dzp));
176211935SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp->z_hints |= ZFS_ACL_TRIVIAL;
17635331Samw 		}
17649179SMark.Shellenbaum@Sun.COM 		mutex_exit(&dzp->z_lock);
17659179SMark.Shellenbaum@Sun.COM 		if (need_chmod) {
176611935SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp->z_hints |= (vap->va_type == VDIR) ?
17679179SMark.Shellenbaum@Sun.COM 			    ZFS_ACL_AUTO_INHERIT : 0;
176812164SMark.Shellenbaum@Sun.COM 			zfs_acl_chmod(zfsvfs, acl_ids->z_mode, acl_ids->z_aclp);
17699179SMark.Shellenbaum@Sun.COM 		}
1770789Sahrens 	}
17715331Samw 
177211935SMark.Shellenbaum@Sun.COM 	if (inherited || vsecp) {
177311935SMark.Shellenbaum@Sun.COM 		acl_ids->z_mode = zfs_mode_compute(acl_ids->z_mode,
177412164SMark.Shellenbaum@Sun.COM 		    acl_ids->z_aclp, &acl_ids->z_aclp->z_hints,
177512164SMark.Shellenbaum@Sun.COM 		    acl_ids->z_fuid, acl_ids->z_fgid);
177611935SMark.Shellenbaum@Sun.COM 		if (ace_trivial_common(acl_ids->z_aclp, 0, zfs_ace_walk) == 0)
177711935SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp->z_hints |= ZFS_ACL_TRIVIAL;
177811935SMark.Shellenbaum@Sun.COM 	}
177911935SMark.Shellenbaum@Sun.COM 
17809179SMark.Shellenbaum@Sun.COM 	return (0);
17819179SMark.Shellenbaum@Sun.COM }
17825331Samw 
17839179SMark.Shellenbaum@Sun.COM /*
17849179SMark.Shellenbaum@Sun.COM  * Free ACL and fuid_infop, but not the acl_ids structure
17859179SMark.Shellenbaum@Sun.COM  */
17869179SMark.Shellenbaum@Sun.COM void
17879179SMark.Shellenbaum@Sun.COM zfs_acl_ids_free(zfs_acl_ids_t *acl_ids)
17889179SMark.Shellenbaum@Sun.COM {
17899179SMark.Shellenbaum@Sun.COM 	if (acl_ids->z_aclp)
17909179SMark.Shellenbaum@Sun.COM 		zfs_acl_free(acl_ids->z_aclp);
17919179SMark.Shellenbaum@Sun.COM 	if (acl_ids->z_fuidp)
17929179SMark.Shellenbaum@Sun.COM 		zfs_fuid_info_free(acl_ids->z_fuidp);
17939179SMark.Shellenbaum@Sun.COM 	acl_ids->z_aclp = NULL;
17949179SMark.Shellenbaum@Sun.COM 	acl_ids->z_fuidp = NULL;
17959179SMark.Shellenbaum@Sun.COM }
17965331Samw 
17979396SMatthew.Ahrens@Sun.COM boolean_t
17989396SMatthew.Ahrens@Sun.COM zfs_acl_ids_overquota(zfsvfs_t *zfsvfs, zfs_acl_ids_t *acl_ids)
17999396SMatthew.Ahrens@Sun.COM {
180011935SMark.Shellenbaum@Sun.COM 	return (zfs_fuid_overquota(zfsvfs, B_FALSE, acl_ids->z_fuid) ||
180111935SMark.Shellenbaum@Sun.COM 	    zfs_fuid_overquota(zfsvfs, B_TRUE, acl_ids->z_fgid));
18029396SMatthew.Ahrens@Sun.COM }
1803789Sahrens 
1804789Sahrens /*
1805789Sahrens  * Retrieve a files ACL
1806789Sahrens  */
1807789Sahrens int
18085331Samw zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
1809789Sahrens {
1810789Sahrens 	zfs_acl_t	*aclp;
18115331Samw 	ulong_t		mask;
1812789Sahrens 	int		error;
18135331Samw 	int 		count = 0;
18145331Samw 	int		largeace = 0;
1815789Sahrens 
18165331Samw 	mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT |
18175331Samw 	    VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES);
18185331Samw 
181911935SMark.Shellenbaum@Sun.COM 	if (mask == 0)
182011935SMark.Shellenbaum@Sun.COM 		return (ENOSYS);
182111935SMark.Shellenbaum@Sun.COM 
18225331Samw 	if (error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr))
18235331Samw 		return (error);
1824789Sahrens 
1825789Sahrens 	mutex_enter(&zp->z_acl_lock);
1826789Sahrens 
18275331Samw 	error = zfs_acl_node_read(zp, &aclp, B_FALSE);
18281544Seschrock 	if (error != 0) {
18291544Seschrock 		mutex_exit(&zp->z_acl_lock);
18301544Seschrock 		return (error);
18311544Seschrock 	}
18321544Seschrock 
18335331Samw 	/*
18345331Samw 	 * Scan ACL to determine number of ACEs
18355331Samw 	 */
183611935SMark.Shellenbaum@Sun.COM 	if ((zp->z_pflags & ZFS_ACL_OBJ_ACE) && !(mask & VSA_ACE_ALLTYPES)) {
18375331Samw 		void *zacep = NULL;
18385331Samw 		uint64_t who;
18395331Samw 		uint32_t access_mask;
18405331Samw 		uint16_t type, iflags;
18415331Samw 
18425331Samw 		while (zacep = zfs_acl_next_ace(aclp, zacep,
18435331Samw 		    &who, &access_mask, &iflags, &type)) {
18445331Samw 			switch (type) {
18455331Samw 			case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
18465331Samw 			case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
18475331Samw 			case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
18485331Samw 			case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
18495331Samw 				largeace++;
18505331Samw 				continue;
18515331Samw 			default:
18525331Samw 				count++;
18535331Samw 			}
18545331Samw 		}
18555331Samw 		vsecp->vsa_aclcnt = count;
18565331Samw 	} else
185711935SMark.Shellenbaum@Sun.COM 		count = (int)aclp->z_acl_count;
1858789Sahrens 
1859789Sahrens 	if (mask & VSA_ACECNT) {
18605331Samw 		vsecp->vsa_aclcnt = count;
1861789Sahrens 	}
1862789Sahrens 
1863789Sahrens 	if (mask & VSA_ACE) {
18645331Samw 		size_t aclsz;
18655331Samw 
18665331Samw 		aclsz = count * sizeof (ace_t) +
18675331Samw 		    sizeof (ace_object_t) * largeace;
18685331Samw 
18695331Samw 		vsecp->vsa_aclentp = kmem_alloc(aclsz, KM_SLEEP);
18705331Samw 		vsecp->vsa_aclentsz = aclsz;
18715331Samw 
18725331Samw 		if (aclp->z_version == ZFS_ACL_VERSION_FUID)
18735771Sjp151216 			zfs_copy_fuid_2_ace(zp->z_zfsvfs, aclp, cr,
18745331Samw 			    vsecp->vsa_aclentp, !(mask & VSA_ACE_ALLTYPES));
18755331Samw 		else {
187610295SMark.Shellenbaum@Sun.COM 			zfs_acl_node_t *aclnode;
187710295SMark.Shellenbaum@Sun.COM 			void *start = vsecp->vsa_aclentp;
187810295SMark.Shellenbaum@Sun.COM 
187910295SMark.Shellenbaum@Sun.COM 			for (aclnode = list_head(&aclp->z_acl); aclnode;
188010295SMark.Shellenbaum@Sun.COM 			    aclnode = list_next(&aclp->z_acl, aclnode)) {
188110295SMark.Shellenbaum@Sun.COM 				bcopy(aclnode->z_acldata, start,
188210295SMark.Shellenbaum@Sun.COM 				    aclnode->z_size);
188310295SMark.Shellenbaum@Sun.COM 				start = (caddr_t)start + aclnode->z_size;
188410295SMark.Shellenbaum@Sun.COM 			}
188510295SMark.Shellenbaum@Sun.COM 			ASSERT((caddr_t)start - (caddr_t)vsecp->vsa_aclentp ==
188610295SMark.Shellenbaum@Sun.COM 			    aclp->z_acl_bytes);
18875331Samw 		}
18885331Samw 	}
18895331Samw 	if (mask & VSA_ACE_ACLFLAGS) {
18905331Samw 		vsecp->vsa_aclflags = 0;
189111935SMark.Shellenbaum@Sun.COM 		if (zp->z_pflags & ZFS_ACL_DEFAULTED)
18925331Samw 			vsecp->vsa_aclflags |= ACL_DEFAULTED;
189311935SMark.Shellenbaum@Sun.COM 		if (zp->z_pflags & ZFS_ACL_PROTECTED)
18945331Samw 			vsecp->vsa_aclflags |= ACL_PROTECTED;
189511935SMark.Shellenbaum@Sun.COM 		if (zp->z_pflags & ZFS_ACL_AUTO_INHERIT)
18965331Samw 			vsecp->vsa_aclflags |= ACL_AUTO_INHERIT;
1897789Sahrens 	}
1898789Sahrens 
1899789Sahrens 	mutex_exit(&zp->z_acl_lock);
1900789Sahrens 
1901789Sahrens 	return (0);
1902789Sahrens }
1903789Sahrens 
19045331Samw int
19055331Samw zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type,
19069179SMark.Shellenbaum@Sun.COM     vsecattr_t *vsecp, cred_t *cr, zfs_fuid_info_t **fuidp, zfs_acl_t **zaclp)
19075331Samw {
19085331Samw 	zfs_acl_t *aclp;
19095331Samw 	zfs_acl_node_t *aclnode;
19105331Samw 	int aclcnt = vsecp->vsa_aclcnt;
19115331Samw 	int error;
19125331Samw 
19135331Samw 	if (vsecp->vsa_aclcnt > MAX_ACL_ENTRIES || vsecp->vsa_aclcnt <= 0)
19145331Samw 		return (EINVAL);
19155331Samw 
19165331Samw 	aclp = zfs_acl_alloc(zfs_acl_version(zfsvfs->z_version));
19175331Samw 
19185331Samw 	aclp->z_hints = 0;
19195331Samw 	aclnode = zfs_acl_node_alloc(aclcnt * sizeof (zfs_object_ace_t));
19205331Samw 	if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) {
19215331Samw 		if ((error = zfs_copy_ace_2_oldace(obj_type, aclp,
19225331Samw 		    (ace_t *)vsecp->vsa_aclentp, aclnode->z_acldata,
19235331Samw 		    aclcnt, &aclnode->z_size)) != 0) {
19245331Samw 			zfs_acl_free(aclp);
19255331Samw 			zfs_acl_node_free(aclnode);
19265331Samw 			return (error);
19275331Samw 		}
19285331Samw 	} else {
19299179SMark.Shellenbaum@Sun.COM 		if ((error = zfs_copy_ace_2_fuid(zfsvfs, obj_type, aclp,
19305331Samw 		    vsecp->vsa_aclentp, aclnode->z_acldata, aclcnt,
19319179SMark.Shellenbaum@Sun.COM 		    &aclnode->z_size, fuidp, cr)) != 0) {
19325331Samw 			zfs_acl_free(aclp);
19335331Samw 			zfs_acl_node_free(aclnode);
19345331Samw 			return (error);
19355331Samw 		}
19365331Samw 	}
19375331Samw 	aclp->z_acl_bytes = aclnode->z_size;
19385331Samw 	aclnode->z_ace_count = aclcnt;
19395331Samw 	aclp->z_acl_count = aclcnt;
19405331Samw 	list_insert_head(&aclp->z_acl, aclnode);
19415331Samw 
19425331Samw 	/*
19435331Samw 	 * If flags are being set then add them to z_hints
19445331Samw 	 */
19455331Samw 	if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS) {
19465331Samw 		if (vsecp->vsa_aclflags & ACL_PROTECTED)
19475331Samw 			aclp->z_hints |= ZFS_ACL_PROTECTED;
19485331Samw 		if (vsecp->vsa_aclflags & ACL_DEFAULTED)
19495331Samw 			aclp->z_hints |= ZFS_ACL_DEFAULTED;
19505331Samw 		if (vsecp->vsa_aclflags & ACL_AUTO_INHERIT)
19515331Samw 			aclp->z_hints |= ZFS_ACL_AUTO_INHERIT;
19525331Samw 	}
19535331Samw 
19545331Samw 	*zaclp = aclp;
19555331Samw 
19565331Samw 	return (0);
19575331Samw }
19585331Samw 
1959789Sahrens /*
1960789Sahrens  * Set a files ACL
1961789Sahrens  */
1962789Sahrens int
19635331Samw zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
1964789Sahrens {
1965789Sahrens 	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
1966789Sahrens 	zilog_t		*zilog = zfsvfs->z_log;
1967789Sahrens 	ulong_t		mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT);
1968789Sahrens 	dmu_tx_t	*tx;
1969789Sahrens 	int		error;
1970789Sahrens 	zfs_acl_t	*aclp;
19715331Samw 	zfs_fuid_info_t	*fuidp = NULL;
19729179SMark.Shellenbaum@Sun.COM 	boolean_t	fuid_dirtied;
1973789Sahrens 
1974789Sahrens 	if (mask == 0)
19754300Smarks 		return (ENOSYS);
1976789Sahrens 
197711935SMark.Shellenbaum@Sun.COM 	if (zp->z_pflags & ZFS_IMMUTABLE)
19785331Samw 		return (EPERM);
19795331Samw 
19805331Samw 	if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))
19815331Samw 		return (error);
19825331Samw 
19839179SMark.Shellenbaum@Sun.COM 	error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, cr, &fuidp,
19849179SMark.Shellenbaum@Sun.COM 	    &aclp);
19855331Samw 	if (error)
19865331Samw 		return (error);
19875331Samw 
19885331Samw 	/*
19895331Samw 	 * If ACL wide flags aren't being set then preserve any
19905331Samw 	 * existing flags.
19915331Samw 	 */
19925331Samw 	if (!(vsecp->vsa_mask & VSA_ACE_ACLFLAGS)) {
199311935SMark.Shellenbaum@Sun.COM 		aclp->z_hints |=
199411935SMark.Shellenbaum@Sun.COM 		    (zp->z_pflags & V4_ACL_WIDE_FLAGS);
19955331Samw 	}
1996789Sahrens top:
1997789Sahrens 	mutex_enter(&zp->z_lock);
1998789Sahrens 	mutex_enter(&zp->z_acl_lock);
1999789Sahrens 
2000789Sahrens 	tx = dmu_tx_create(zfsvfs->z_os);
200111935SMark.Shellenbaum@Sun.COM 
200211935SMark.Shellenbaum@Sun.COM 	dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
200311935SMark.Shellenbaum@Sun.COM 
20049179SMark.Shellenbaum@Sun.COM 	fuid_dirtied = zfsvfs->z_fuid_dirty;
20059396SMatthew.Ahrens@Sun.COM 	if (fuid_dirtied)
20069396SMatthew.Ahrens@Sun.COM 		zfs_fuid_txhold(zfsvfs, tx);
2007789Sahrens 
200811935SMark.Shellenbaum@Sun.COM 	/*
200911935SMark.Shellenbaum@Sun.COM 	 * If old version and ACL won't fit in bonus and we aren't
201011935SMark.Shellenbaum@Sun.COM 	 * upgrading then take out necessary DMU holds
201111935SMark.Shellenbaum@Sun.COM 	 */
201211935SMark.Shellenbaum@Sun.COM 
201311935SMark.Shellenbaum@Sun.COM 	if (ZFS_EXTERNAL_ACL(zp)) {
201411935SMark.Shellenbaum@Sun.COM 		if (zfsvfs->z_version <= ZPL_VERSION_SA &&
201511935SMark.Shellenbaum@Sun.COM 		    ZNODE_ACL_VERSION(zp) <= ZFS_ACL_VERSION_INITIAL) {
201611935SMark.Shellenbaum@Sun.COM 			dmu_tx_hold_free(tx, ZFS_EXTERNAL_ACL(zp), 0,
201711935SMark.Shellenbaum@Sun.COM 			    DMU_OBJECT_END);
201811935SMark.Shellenbaum@Sun.COM 		} else {
201911935SMark.Shellenbaum@Sun.COM 			dmu_tx_hold_write(tx, ZFS_EXTERNAL_ACL(zp),
202011935SMark.Shellenbaum@Sun.COM 			    0, aclp->z_acl_bytes);
202111935SMark.Shellenbaum@Sun.COM 		}
202211935SMark.Shellenbaum@Sun.COM 	} else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) {
202311935SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes);
202411935SMark.Shellenbaum@Sun.COM 	}
202511935SMark.Shellenbaum@Sun.COM 
202611935SMark.Shellenbaum@Sun.COM 	zfs_sa_upgrade_txholds(tx, zp);
20278227SNeil.Perrin@Sun.COM 	error = dmu_tx_assign(tx, TXG_NOWAIT);
2028789Sahrens 	if (error) {
2029789Sahrens 		mutex_exit(&zp->z_acl_lock);
2030789Sahrens 		mutex_exit(&zp->z_lock);
2031789Sahrens 
20328227SNeil.Perrin@Sun.COM 		if (error == ERESTART) {
20332113Sahrens 			dmu_tx_wait(tx);
20342113Sahrens 			dmu_tx_abort(tx);
2035789Sahrens 			goto top;
2036789Sahrens 		}
20372113Sahrens 		dmu_tx_abort(tx);
20385331Samw 		zfs_acl_free(aclp);
2039789Sahrens 		return (error);
2040789Sahrens 	}
2041789Sahrens 
20429179SMark.Shellenbaum@Sun.COM 	error = zfs_aclset_common(zp, aclp, cr, tx);
2043789Sahrens 	ASSERT(error == 0);
204410143STim.Haley@Sun.COM 	zp->z_acl_cached = aclp;
2045789Sahrens 
20469179SMark.Shellenbaum@Sun.COM 	if (fuid_dirtied)
20479179SMark.Shellenbaum@Sun.COM 		zfs_fuid_sync(zfsvfs, tx);
20489179SMark.Shellenbaum@Sun.COM 
20495331Samw 	zfs_log_acl(zilog, tx, zp, vsecp, fuidp);
20505331Samw 
20515331Samw 	if (fuidp)
20525331Samw 		zfs_fuid_info_free(fuidp);
2053789Sahrens 	dmu_tx_commit(tx);
2054789Sahrens done:
2055789Sahrens 	mutex_exit(&zp->z_acl_lock);
2056789Sahrens 	mutex_exit(&zp->z_lock);
2057789Sahrens 
2058789Sahrens 	return (error);
2059789Sahrens }
2060789Sahrens 
20615331Samw /*
20629749STim.Haley@Sun.COM  * Check accesses of interest (AoI) against attributes of the dataset
20639749STim.Haley@Sun.COM  * such as read-only.  Returns zero if no AoI conflict with dataset
20649749STim.Haley@Sun.COM  * attributes, otherwise an appropriate errno is returned.
20655331Samw  */
2066789Sahrens static int
20679749STim.Haley@Sun.COM zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
2068789Sahrens {
20699749STim.Haley@Sun.COM 	if ((v4_mode & WRITE_MASK) &&
20709749STim.Haley@Sun.COM 	    (zp->z_zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) &&
20719749STim.Haley@Sun.COM 	    (!IS_DEVVP(ZTOV(zp)) ||
20729749STim.Haley@Sun.COM 	    (IS_DEVVP(ZTOV(zp)) && (v4_mode & WRITE_MASK_ATTRS)))) {
20739749STim.Haley@Sun.COM 		return (EROFS);
20749749STim.Haley@Sun.COM 	}
20759749STim.Haley@Sun.COM 
20769749STim.Haley@Sun.COM 	/*
20779749STim.Haley@Sun.COM 	 * Only check for READONLY on non-directories.
20789749STim.Haley@Sun.COM 	 */
20799749STim.Haley@Sun.COM 	if ((v4_mode & WRITE_MASK_DATA) &&
20809749STim.Haley@Sun.COM 	    (((ZTOV(zp)->v_type != VDIR) &&
208111935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
20829749STim.Haley@Sun.COM 	    (ZTOV(zp)->v_type == VDIR &&
208311935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & ZFS_IMMUTABLE)))) {
20849749STim.Haley@Sun.COM 		return (EPERM);
20859749STim.Haley@Sun.COM 	}
20869749STim.Haley@Sun.COM 
20879749STim.Haley@Sun.COM 	if ((v4_mode & (ACE_DELETE | ACE_DELETE_CHILD)) &&
208811935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & ZFS_NOUNLINK)) {
20899749STim.Haley@Sun.COM 		return (EPERM);
20909749STim.Haley@Sun.COM 	}
20919749STim.Haley@Sun.COM 
20929749STim.Haley@Sun.COM 	if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) &&
209311935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & ZFS_AV_QUARANTINED))) {
20949749STim.Haley@Sun.COM 		return (EACCES);
20959749STim.Haley@Sun.COM 	}
20969749STim.Haley@Sun.COM 
20979749STim.Haley@Sun.COM 	return (0);
20989749STim.Haley@Sun.COM }
20999749STim.Haley@Sun.COM 
21009749STim.Haley@Sun.COM /*
21019749STim.Haley@Sun.COM  * The primary usage of this function is to loop through all of the
21029749STim.Haley@Sun.COM  * ACEs in the znode, determining what accesses of interest (AoI) to
21039749STim.Haley@Sun.COM  * the caller are allowed or denied.  The AoI are expressed as bits in
21049749STim.Haley@Sun.COM  * the working_mode parameter.  As each ACE is processed, bits covered
21059749STim.Haley@Sun.COM  * by that ACE are removed from the working_mode.  This removal
21069749STim.Haley@Sun.COM  * facilitates two things.  The first is that when the working mode is
21079749STim.Haley@Sun.COM  * empty (= 0), we know we've looked at all the AoI. The second is
21089749STim.Haley@Sun.COM  * that the ACE interpretation rules don't allow a later ACE to undo
21099749STim.Haley@Sun.COM  * something granted or denied by an earlier ACE.  Removing the
21109749STim.Haley@Sun.COM  * discovered access or denial enforces this rule.  At the end of
21119749STim.Haley@Sun.COM  * processing the ACEs, all AoI that were found to be denied are
21129749STim.Haley@Sun.COM  * placed into the working_mode, giving the caller a mask of denied
21139749STim.Haley@Sun.COM  * accesses.  Returns:
21149749STim.Haley@Sun.COM  *	0		if all AoI granted
21159749STim.Haley@Sun.COM  *	EACCESS 	if the denied mask is non-zero
21169749STim.Haley@Sun.COM  *	other error	if abnormal failure (e.g., IO error)
21179749STim.Haley@Sun.COM  *
21189749STim.Haley@Sun.COM  * A secondary usage of the function is to determine if any of the
21199749STim.Haley@Sun.COM  * AoI are granted.  If an ACE grants any access in
21209749STim.Haley@Sun.COM  * the working_mode, we immediately short circuit out of the function.
21219749STim.Haley@Sun.COM  * This mode is chosen by setting anyaccess to B_TRUE.  The
21229749STim.Haley@Sun.COM  * working_mode is not a denied access mask upon exit if the function
21239749STim.Haley@Sun.COM  * is used in this manner.
21249749STim.Haley@Sun.COM  */
21259749STim.Haley@Sun.COM static int
21269749STim.Haley@Sun.COM zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
21279749STim.Haley@Sun.COM     boolean_t anyaccess, cred_t *cr)
21289749STim.Haley@Sun.COM {
21299749STim.Haley@Sun.COM 	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
2130789Sahrens 	zfs_acl_t	*aclp;
21311544Seschrock 	int		error;
2132789Sahrens 	uid_t		uid = crgetuid(cr);
21335331Samw 	uint64_t 	who;
21345331Samw 	uint16_t	type, iflags;
21355331Samw 	uint16_t	entry_type;
21365331Samw 	uint32_t	access_mask;
21376056Smarks 	uint32_t	deny_mask = 0;
21385331Samw 	zfs_ace_hdr_t	*acep = NULL;
21395331Samw 	boolean_t	checkit;
214011935SMark.Shellenbaum@Sun.COM 	uint64_t	gowner;
21415331Samw 
2142789Sahrens 	mutex_enter(&zp->z_acl_lock);
2143789Sahrens 
21445331Samw 	error = zfs_acl_node_read(zp, &aclp, B_FALSE);
21451544Seschrock 	if (error != 0) {
21461544Seschrock 		mutex_exit(&zp->z_acl_lock);
21471544Seschrock 		return (error);
21481544Seschrock 	}
21491544Seschrock 
215011935SMark.Shellenbaum@Sun.COM 	ASSERT(zp->z_acl_cached);
215111935SMark.Shellenbaum@Sun.COM 
215211935SMark.Shellenbaum@Sun.COM 	if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_GID(zfsvfs),
215312218SMark.Shellenbaum@Sun.COM 	    &gowner, sizeof (gowner))) != 0) {
215412218SMark.Shellenbaum@Sun.COM 		mutex_exit(&zp->z_acl_lock);
215511935SMark.Shellenbaum@Sun.COM 		return (error);
215612218SMark.Shellenbaum@Sun.COM 	}
215711935SMark.Shellenbaum@Sun.COM 
21585331Samw 	while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
21595331Samw 	    &iflags, &type)) {
21609749STim.Haley@Sun.COM 		uint32_t mask_matched;
2161789Sahrens 
21627559SMark.Shellenbaum@Sun.COM 		if (!zfs_acl_valid_ace_type(type, iflags))
21637559SMark.Shellenbaum@Sun.COM 			continue;
21647559SMark.Shellenbaum@Sun.COM 
21657057Smarks 		if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
2166789Sahrens 			continue;
2167789Sahrens 
21689749STim.Haley@Sun.COM 		/* Skip ACE if it does not affect any AoI */
21699749STim.Haley@Sun.COM 		mask_matched = (access_mask & *working_mode);
21709749STim.Haley@Sun.COM 		if (!mask_matched)
21719749STim.Haley@Sun.COM 			continue;
21729749STim.Haley@Sun.COM 
21735331Samw 		entry_type = (iflags & ACE_TYPE_FLAGS);
21745331Samw 
21755331Samw 		checkit = B_FALSE;
21765331Samw 
2177789Sahrens 		switch (entry_type) {
2178789Sahrens 		case ACE_OWNER:
217911935SMark.Shellenbaum@Sun.COM 			if (uid == zp->z_uid)
21805331Samw 				checkit = B_TRUE;
2181789Sahrens 			break;
21825331Samw 		case OWNING_GROUP:
21835331Samw 			who = gowner;
21845331Samw 			/*FALLTHROUGH*/
2185789Sahrens 		case ACE_IDENTIFIER_GROUP:
21865331Samw 			checkit = zfs_groupmember(zfsvfs, who, cr);
2187789Sahrens 			break;
2188789Sahrens 		case ACE_EVERYONE:
21895331Samw 			checkit = B_TRUE;
2190789Sahrens 			break;
2191789Sahrens 
2192789Sahrens 		/* USER Entry */
2193789Sahrens 		default:
2194789Sahrens 			if (entry_type == 0) {
21955331Samw 				uid_t newid;
21965331Samw 
21975959Smarks 				newid = zfs_fuid_map_id(zfsvfs, who, cr,
21985959Smarks 				    ZFS_ACE_USER);
21995331Samw 				if (newid != IDMAP_WK_CREATOR_OWNER_UID &&
22005331Samw 				    uid == newid)
22015331Samw 					checkit = B_TRUE;
2202789Sahrens 				break;
22035331Samw 			} else {
22045331Samw 				mutex_exit(&zp->z_acl_lock);
22055331Samw 				return (EIO);
2206789Sahrens 			}
22075331Samw 		}
22085331Samw 
22095331Samw 		if (checkit) {
22109749STim.Haley@Sun.COM 			if (type == DENY) {
22119749STim.Haley@Sun.COM 				DTRACE_PROBE3(zfs__ace__denies,
22129749STim.Haley@Sun.COM 				    znode_t *, zp,
22139749STim.Haley@Sun.COM 				    zfs_ace_hdr_t *, acep,
22149749STim.Haley@Sun.COM 				    uint32_t, mask_matched);
22159749STim.Haley@Sun.COM 				deny_mask |= mask_matched;
22169749STim.Haley@Sun.COM 			} else {
22179749STim.Haley@Sun.COM 				DTRACE_PROBE3(zfs__ace__allows,
22189749STim.Haley@Sun.COM 				    znode_t *, zp,
22199749STim.Haley@Sun.COM 				    zfs_ace_hdr_t *, acep,
22209749STim.Haley@Sun.COM 				    uint32_t, mask_matched);
22219749STim.Haley@Sun.COM 				if (anyaccess) {
22229749STim.Haley@Sun.COM 					mutex_exit(&zp->z_acl_lock);
22239749STim.Haley@Sun.COM 					return (0);
22249749STim.Haley@Sun.COM 				}
22255331Samw 			}
22269749STim.Haley@Sun.COM 			*working_mode &= ~mask_matched;
2227789Sahrens 		}
2228789Sahrens 
22296056Smarks 		/* Are we done? */
22306056Smarks 		if (*working_mode == 0)
2231789Sahrens 			break;
2232789Sahrens 	}
2233789Sahrens 
2234789Sahrens 	mutex_exit(&zp->z_acl_lock);
22356056Smarks 
22366056Smarks 	/* Put the found 'denies' back on the working mode */
22377163Smarks 	if (deny_mask) {
22387163Smarks 		*working_mode |= deny_mask;
22396056Smarks 		return (EACCES);
22407163Smarks 	} else if (*working_mode) {
22417163Smarks 		return (-1);
22427163Smarks 	}
22436056Smarks 
22446056Smarks 	return (0);
2245789Sahrens }
2246789Sahrens 
22479749STim.Haley@Sun.COM /*
22489749STim.Haley@Sun.COM  * Return true if any access whatsoever granted, we don't actually
22499749STim.Haley@Sun.COM  * care what access is granted.
22509749STim.Haley@Sun.COM  */
22519749STim.Haley@Sun.COM boolean_t
22529749STim.Haley@Sun.COM zfs_has_access(znode_t *zp, cred_t *cr)
22539749STim.Haley@Sun.COM {
22549749STim.Haley@Sun.COM 	uint32_t have = ACE_ALL_PERMS;
22559749STim.Haley@Sun.COM 
22569749STim.Haley@Sun.COM 	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
225712273SCasper.Dik@Sun.COM 		return (secpolicy_vnode_any_access(cr, ZTOV(zp),
225812273SCasper.Dik@Sun.COM 		    zp->z_uid) == 0);
22599749STim.Haley@Sun.COM 	}
22609749STim.Haley@Sun.COM 	return (B_TRUE);
22619749STim.Haley@Sun.COM }
22629749STim.Haley@Sun.COM 
22639749STim.Haley@Sun.COM static int
22649749STim.Haley@Sun.COM zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
22659749STim.Haley@Sun.COM     boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
22669749STim.Haley@Sun.COM {
22679749STim.Haley@Sun.COM 	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
22689749STim.Haley@Sun.COM 	int err;
22699749STim.Haley@Sun.COM 
22709749STim.Haley@Sun.COM 	*working_mode = v4_mode;
22719749STim.Haley@Sun.COM 	*check_privs = B_TRUE;
22729749STim.Haley@Sun.COM 
22739749STim.Haley@Sun.COM 	/*
22749749STim.Haley@Sun.COM 	 * Short circuit empty requests
22759749STim.Haley@Sun.COM 	 */
22769749STim.Haley@Sun.COM 	if (v4_mode == 0 || zfsvfs->z_replay) {
22779749STim.Haley@Sun.COM 		*working_mode = 0;
22789749STim.Haley@Sun.COM 		return (0);
22799749STim.Haley@Sun.COM 	}
22809749STim.Haley@Sun.COM 
22819749STim.Haley@Sun.COM 	if ((err = zfs_zaccess_dataset_check(zp, v4_mode)) != 0) {
22829749STim.Haley@Sun.COM 		*check_privs = B_FALSE;
22839749STim.Haley@Sun.COM 		return (err);
22849749STim.Haley@Sun.COM 	}
22859749STim.Haley@Sun.COM 
22869749STim.Haley@Sun.COM 	/*
22879749STim.Haley@Sun.COM 	 * The caller requested that the ACL check be skipped.  This
22889749STim.Haley@Sun.COM 	 * would only happen if the caller checked VOP_ACCESS() with a
22899749STim.Haley@Sun.COM 	 * 32 bit ACE mask and already had the appropriate permissions.
22909749STim.Haley@Sun.COM 	 */
22919749STim.Haley@Sun.COM 	if (skipaclchk) {
22929749STim.Haley@Sun.COM 		*working_mode = 0;
22939749STim.Haley@Sun.COM 		return (0);
22949749STim.Haley@Sun.COM 	}
22959749STim.Haley@Sun.COM 
22969749STim.Haley@Sun.COM 	return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
22979749STim.Haley@Sun.COM }
22989749STim.Haley@Sun.COM 
22995331Samw static int
23005331Samw zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
23015331Samw     cred_t *cr)
23025331Samw {
23035331Samw 	if (*working_mode != ACE_WRITE_DATA)
23045331Samw 		return (EACCES);
23055331Samw 
23065331Samw 	return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode,
23075331Samw 	    check_privs, B_FALSE, cr));
23085331Samw }
2309789Sahrens 
23109981STim.Haley@Sun.COM int
23119981STim.Haley@Sun.COM zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
23129981STim.Haley@Sun.COM {
23139981STim.Haley@Sun.COM 	boolean_t owner = B_FALSE;
23149981STim.Haley@Sun.COM 	boolean_t groupmbr = B_FALSE;
23159981STim.Haley@Sun.COM 	boolean_t is_attr;
23169981STim.Haley@Sun.COM 	uid_t uid = crgetuid(cr);
23179981STim.Haley@Sun.COM 	int error;
23189981STim.Haley@Sun.COM 
231911935SMark.Shellenbaum@Sun.COM 	if (zdp->z_pflags & ZFS_AV_QUARANTINED)
23209981STim.Haley@Sun.COM 		return (EACCES);
23219981STim.Haley@Sun.COM 
232211935SMark.Shellenbaum@Sun.COM 	is_attr = ((zdp->z_pflags & ZFS_XATTR) &&
23239981STim.Haley@Sun.COM 	    (ZTOV(zdp)->v_type == VDIR));
23249981STim.Haley@Sun.COM 	if (is_attr)
23259981STim.Haley@Sun.COM 		goto slow;
23269981STim.Haley@Sun.COM 
232711935SMark.Shellenbaum@Sun.COM 
23289981STim.Haley@Sun.COM 	mutex_enter(&zdp->z_acl_lock);
23299981STim.Haley@Sun.COM 
233011935SMark.Shellenbaum@Sun.COM 	if (zdp->z_pflags & ZFS_NO_EXECS_DENIED) {
23319981STim.Haley@Sun.COM 		mutex_exit(&zdp->z_acl_lock);
23329981STim.Haley@Sun.COM 		return (0);
23339981STim.Haley@Sun.COM 	}
23349981STim.Haley@Sun.COM 
233511935SMark.Shellenbaum@Sun.COM 	if (IS_EPHEMERAL(zdp->z_uid) != 0 || IS_EPHEMERAL(zdp->z_gid) != 0) {
23369981STim.Haley@Sun.COM 		mutex_exit(&zdp->z_acl_lock);
23379981STim.Haley@Sun.COM 		goto slow;
23389981STim.Haley@Sun.COM 	}
23399981STim.Haley@Sun.COM 
234011935SMark.Shellenbaum@Sun.COM 	if (uid == zdp->z_uid) {
23419981STim.Haley@Sun.COM 		owner = B_TRUE;
234211935SMark.Shellenbaum@Sun.COM 		if (zdp->z_mode & S_IXUSR) {
23439981STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
23449981STim.Haley@Sun.COM 			return (0);
234510232STim.Haley@Sun.COM 		} else {
234610232STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
234710232STim.Haley@Sun.COM 			goto slow;
23489981STim.Haley@Sun.COM 		}
23499981STim.Haley@Sun.COM 	}
235011935SMark.Shellenbaum@Sun.COM 	if (groupmember(zdp->z_gid, cr)) {
23519981STim.Haley@Sun.COM 		groupmbr = B_TRUE;
235211935SMark.Shellenbaum@Sun.COM 		if (zdp->z_mode & S_IXGRP) {
23539981STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
23549981STim.Haley@Sun.COM 			return (0);
235510232STim.Haley@Sun.COM 		} else {
235610232STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
235710232STim.Haley@Sun.COM 			goto slow;
23589981STim.Haley@Sun.COM 		}
23599981STim.Haley@Sun.COM 	}
23609981STim.Haley@Sun.COM 	if (!owner && !groupmbr) {
236111935SMark.Shellenbaum@Sun.COM 		if (zdp->z_mode & S_IXOTH) {
23629981STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
23639981STim.Haley@Sun.COM 			return (0);
23649981STim.Haley@Sun.COM 		}
23659981STim.Haley@Sun.COM 	}
23669981STim.Haley@Sun.COM 
23679981STim.Haley@Sun.COM 	mutex_exit(&zdp->z_acl_lock);
23689981STim.Haley@Sun.COM 
23699981STim.Haley@Sun.COM slow:
23709981STim.Haley@Sun.COM 	DTRACE_PROBE(zfs__fastpath__execute__access__miss);
23719981STim.Haley@Sun.COM 	ZFS_ENTER(zdp->z_zfsvfs);
23729981STim.Haley@Sun.COM 	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr);
23739981STim.Haley@Sun.COM 	ZFS_EXIT(zdp->z_zfsvfs);
23749981STim.Haley@Sun.COM 	return (error);
23759981STim.Haley@Sun.COM }
23769981STim.Haley@Sun.COM 
2377789Sahrens /*
237812273SCasper.Dik@Sun.COM  * Determine whether Access should be granted/denied.
237912273SCasper.Dik@Sun.COM  * The least priv subsytem is always consulted as a basic privilege
238012273SCasper.Dik@Sun.COM  * can define any form of access.
2381789Sahrens  */
2382789Sahrens int
23835331Samw zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
2384789Sahrens {
23855331Samw 	uint32_t	working_mode;
23865331Samw 	int		error;
23875331Samw 	int		is_attr;
23885331Samw 	boolean_t 	check_privs;
23895331Samw 	znode_t		*xzp;
23905331Samw 	znode_t 	*check_zp = zp;
239112273SCasper.Dik@Sun.COM 	mode_t		needed_bits;
2392789Sahrens 
239311935SMark.Shellenbaum@Sun.COM 	is_attr = ((zp->z_pflags & ZFS_XATTR) && (ZTOV(zp)->v_type == VDIR));
2394789Sahrens 
2395789Sahrens 	/*
2396789Sahrens 	 * If attribute then validate against base file
2397789Sahrens 	 */
2398789Sahrens 	if (is_attr) {
239911935SMark.Shellenbaum@Sun.COM 		uint64_t	parent;
240011935SMark.Shellenbaum@Sun.COM 
240111935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl,
240211935SMark.Shellenbaum@Sun.COM 		    SA_ZPL_PARENT(zp->z_zfsvfs), &parent,
240311935SMark.Shellenbaum@Sun.COM 		    sizeof (parent))) != 0)
240411935SMark.Shellenbaum@Sun.COM 			return (error);
240511935SMark.Shellenbaum@Sun.COM 
2406789Sahrens 		if ((error = zfs_zget(zp->z_zfsvfs,
240711935SMark.Shellenbaum@Sun.COM 		    parent, &xzp)) != 0)	{
2408789Sahrens 			return (error);
2409789Sahrens 		}
24105331Samw 
2411789Sahrens 		check_zp = xzp;
24125331Samw 
2413789Sahrens 		/*
2414789Sahrens 		 * fixup mode to map to xattr perms
2415789Sahrens 		 */
2416789Sahrens 
2417789Sahrens 		if (mode & (ACE_WRITE_DATA|ACE_APPEND_DATA)) {
2418789Sahrens 			mode &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
2419789Sahrens 			mode |= ACE_WRITE_NAMED_ATTRS;
2420789Sahrens 		}
2421789Sahrens 
2422789Sahrens 		if (mode & (ACE_READ_DATA|ACE_EXECUTE)) {
2423789Sahrens 			mode &= ~(ACE_READ_DATA|ACE_EXECUTE);
2424789Sahrens 			mode |= ACE_READ_NAMED_ATTRS;
2425789Sahrens 		}
2426789Sahrens 	}
2427789Sahrens 
242812273SCasper.Dik@Sun.COM 	/*
242912273SCasper.Dik@Sun.COM 	 * Map the bits required to the standard vnode flags VREAD|VWRITE|VEXEC
243012273SCasper.Dik@Sun.COM 	 * in needed_bits.  Map the bits mapped by working_mode (currently
243112273SCasper.Dik@Sun.COM 	 * missing) in missing_bits.
243212273SCasper.Dik@Sun.COM 	 * Call secpolicy_vnode_access2() with (needed_bits & ~checkmode),
243312273SCasper.Dik@Sun.COM 	 * needed_bits.
243412273SCasper.Dik@Sun.COM 	 */
243512273SCasper.Dik@Sun.COM 	needed_bits = 0;
243612273SCasper.Dik@Sun.COM 
243712273SCasper.Dik@Sun.COM 	working_mode = mode;
243812273SCasper.Dik@Sun.COM 	if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) &&
243912273SCasper.Dik@Sun.COM 	    zp->z_uid == crgetuid(cr))
244012273SCasper.Dik@Sun.COM 		working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES);
244112273SCasper.Dik@Sun.COM 
244212273SCasper.Dik@Sun.COM 	if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS|
244312273SCasper.Dik@Sun.COM 	    ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE))
244412273SCasper.Dik@Sun.COM 		needed_bits |= VREAD;
244512273SCasper.Dik@Sun.COM 	if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS|
244612273SCasper.Dik@Sun.COM 	    ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE))
244712273SCasper.Dik@Sun.COM 		needed_bits |= VWRITE;
244812273SCasper.Dik@Sun.COM 	if (working_mode & ACE_EXECUTE)
244912273SCasper.Dik@Sun.COM 		needed_bits |= VEXEC;
245012273SCasper.Dik@Sun.COM 
24515331Samw 	if ((error = zfs_zaccess_common(check_zp, mode, &working_mode,
24525331Samw 	    &check_privs, skipaclchk, cr)) == 0) {
24535331Samw 		if (is_attr)
24545331Samw 			VN_RELE(ZTOV(xzp));
245512273SCasper.Dik@Sun.COM 		return (secpolicy_vnode_access2(cr, ZTOV(zp), zp->z_uid,
245612273SCasper.Dik@Sun.COM 		    needed_bits, needed_bits));
24575331Samw 	}
2458789Sahrens 
24595959Smarks 	if (error && !check_privs) {
2460789Sahrens 		if (is_attr)
2461789Sahrens 			VN_RELE(ZTOV(xzp));
2462789Sahrens 		return (error);
2463789Sahrens 	}
2464789Sahrens 
24655331Samw 	if (error && (flags & V_APPEND)) {
24665331Samw 		error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
24675331Samw 	}
24685331Samw 
24695331Samw 	if (error && check_privs) {
24705331Samw 		mode_t		checkmode = 0;
24715331Samw 
24725331Samw 		/*
24735331Samw 		 * First check for implicit owner permission on
24745331Samw 		 * read_acl/read_attributes
24755331Samw 		 */
24765331Samw 
24775331Samw 		error = 0;
24785331Samw 		ASSERT(working_mode != 0);
24795331Samw 
24805331Samw 		if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) &&
248111935SMark.Shellenbaum@Sun.COM 		    zp->z_uid == crgetuid(cr)))
24825331Samw 			working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES);
24835331Samw 
24845331Samw 		if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS|
24857624SMark.Shellenbaum@Sun.COM 		    ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE))
24865331Samw 			checkmode |= VREAD;
24875331Samw 		if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS|
24887624SMark.Shellenbaum@Sun.COM 		    ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE))
24895331Samw 			checkmode |= VWRITE;
24905331Samw 		if (working_mode & ACE_EXECUTE)
24915331Samw 			checkmode |= VEXEC;
24925331Samw 
249312273SCasper.Dik@Sun.COM 		error = secpolicy_vnode_access2(cr, ZTOV(check_zp), zp->z_uid,
249412273SCasper.Dik@Sun.COM 		    needed_bits & ~checkmode, needed_bits);
24955331Samw 
24965331Samw 		if (error == 0 && (working_mode & ACE_WRITE_OWNER))
249711935SMark.Shellenbaum@Sun.COM 			error = secpolicy_vnode_chown(cr, zp->z_uid);
24985331Samw 		if (error == 0 && (working_mode & ACE_WRITE_ACL))
249911935SMark.Shellenbaum@Sun.COM 			error = secpolicy_vnode_setdac(cr, zp->z_uid);
25005331Samw 
25015331Samw 		if (error == 0 && (working_mode &
25025331Samw 		    (ACE_DELETE|ACE_DELETE_CHILD)))
25035331Samw 			error = secpolicy_vnode_remove(cr);
25045331Samw 
25057624SMark.Shellenbaum@Sun.COM 		if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) {
250611935SMark.Shellenbaum@Sun.COM 			error = secpolicy_vnode_chown(cr, zp->z_uid);
25077624SMark.Shellenbaum@Sun.COM 		}
25085331Samw 		if (error == 0) {
25095331Samw 			/*
25105331Samw 			 * See if any bits other than those already checked
25115331Samw 			 * for are still present.  If so then return EACCES
25125331Samw 			 */
25135331Samw 			if (working_mode & ~(ZFS_CHECKED_MASKS)) {
25145331Samw 				error = EACCES;
25155331Samw 			}
25165331Samw 		}
251712273SCasper.Dik@Sun.COM 	} else if (error == 0) {
251812273SCasper.Dik@Sun.COM 		error = secpolicy_vnode_access2(cr, ZTOV(zp), zp->z_uid,
251912273SCasper.Dik@Sun.COM 		    needed_bits, needed_bits);
2520789Sahrens 	}
2521789Sahrens 
252212273SCasper.Dik@Sun.COM 
2523789Sahrens 	if (is_attr)
2524789Sahrens 		VN_RELE(ZTOV(xzp));
2525789Sahrens 
2526789Sahrens 	return (error);
2527789Sahrens }
2528789Sahrens 
2529789Sahrens /*
25305331Samw  * Translate traditional unix VREAD/VWRITE/VEXEC mode into
25315331Samw  * native ACL format and call zfs_zaccess()
2532789Sahrens  */
2533789Sahrens int
25345331Samw zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
2535789Sahrens {
25365331Samw 	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
2537789Sahrens }
2538789Sahrens 
2539789Sahrens /*
25405331Samw  * Access function for secpolicy_vnode_setattr
2541789Sahrens  */
2542789Sahrens int
25435331Samw zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
2544789Sahrens {
2545789Sahrens 	int v4_mode = zfs_unix_to_v4(mode >> 6);
2546789Sahrens 
25475331Samw 	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
2548789Sahrens }
2549789Sahrens 
25502604Smarks static int
25516257Smarks zfs_delete_final_check(znode_t *zp, znode_t *dzp,
255212273SCasper.Dik@Sun.COM     mode_t available_perms, cred_t *cr)
25532604Smarks {
25542604Smarks 	int error;
255511935SMark.Shellenbaum@Sun.COM 
255612273SCasper.Dik@Sun.COM 	error = secpolicy_vnode_access2(cr, ZTOV(dzp),
255712273SCasper.Dik@Sun.COM 	    dzp->z_uid, available_perms, VWRITE|VEXEC);
25582604Smarks 
25592604Smarks 	if (error == 0)
25602604Smarks 		error = zfs_sticky_remove_access(dzp, zp, cr);
25612604Smarks 
25622604Smarks 	return (error);
25632604Smarks }
25642604Smarks 
2565789Sahrens /*
2566789Sahrens  * Determine whether Access should be granted/deny, without
2567789Sahrens  * consulting least priv subsystem.
2568789Sahrens  *
2569789Sahrens  *
2570789Sahrens  * The following chart is the recommended NFSv4 enforcement for
2571789Sahrens  * ability to delete an object.
2572789Sahrens  *
2573789Sahrens  *      -------------------------------------------------------
2574789Sahrens  *      |   Parent Dir  |           Target Object Permissions |
2575789Sahrens  *      |  permissions  |                                     |
2576789Sahrens  *      -------------------------------------------------------
2577789Sahrens  *      |               | ACL Allows | ACL Denies| Delete     |
2578789Sahrens  *      |               |  Delete    |  Delete   | unspecified|
2579789Sahrens  *      -------------------------------------------------------
2580789Sahrens  *      |  ACL Allows   | Permit     | Permit    | Permit     |
2581789Sahrens  *      |  DELETE_CHILD |                                     |
2582789Sahrens  *      -------------------------------------------------------
2583789Sahrens  *      |  ACL Denies   | Permit     | Deny      | Deny       |
2584789Sahrens  *      |  DELETE_CHILD |            |           |            |
2585789Sahrens  *      -------------------------------------------------------
2586789Sahrens  *      | ACL specifies |            |           |            |
2587789Sahrens  *      | only allow    | Permit     | Permit    | Permit     |
2588789Sahrens  *      | write and     |            |           |            |
2589789Sahrens  *      | execute       |            |           |            |
2590789Sahrens  *      -------------------------------------------------------
2591789Sahrens  *      | ACL denies    |            |           |            |
2592789Sahrens  *      | write and     | Permit     | Deny      | Deny       |
2593789Sahrens  *      | execute       |            |           |            |
2594789Sahrens  *      -------------------------------------------------------
2595789Sahrens  *         ^
2596789Sahrens  *         |
2597789Sahrens  *         No search privilege, can't even look up file?
2598789Sahrens  *
2599789Sahrens  */
2600789Sahrens int
2601789Sahrens zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
2602789Sahrens {
26035331Samw 	uint32_t dzp_working_mode = 0;
26045331Samw 	uint32_t zp_working_mode = 0;
2605789Sahrens 	int dzp_error, zp_error;
260612273SCasper.Dik@Sun.COM 	mode_t available_perms;
26075331Samw 	boolean_t dzpcheck_privs = B_TRUE;
26085331Samw 	boolean_t zpcheck_privs = B_TRUE;
2609789Sahrens 
2610789Sahrens 	/*
26116257Smarks 	 * We want specific DELETE permissions to
2612789Sahrens 	 * take precedence over WRITE/EXECUTE.  We don't
2613789Sahrens 	 * want an ACL such as this to mess us up.
26142604Smarks 	 * user:joe:write_data:deny,user:joe:delete:allow
2615789Sahrens 	 *
2616789Sahrens 	 * However, deny permissions may ultimately be overridden
2617789Sahrens 	 * by secpolicy_vnode_access().
26186257Smarks 	 *
26196257Smarks 	 * We will ask for all of the necessary permissions and then
26206257Smarks 	 * look at the working modes from the directory and target object
26216257Smarks 	 * to determine what was found.
2622789Sahrens 	 */
2623789Sahrens 
262411935SMark.Shellenbaum@Sun.COM 	if (zp->z_pflags & (ZFS_IMMUTABLE | ZFS_NOUNLINK))
26255331Samw 		return (EPERM);
26265331Samw 
26276257Smarks 	/*
26287163Smarks 	 * First row
26296257Smarks 	 * If the directory permissions allow the delete, we are done.
26306257Smarks 	 */
26317163Smarks 	if ((dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD,
26326257Smarks 	    &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
26336257Smarks 		return (0);
2634789Sahrens 
26356257Smarks 	/*
26366257Smarks 	 * If target object has delete permission then we are done
26376257Smarks 	 */
26386257Smarks 	if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
26396257Smarks 	    &zpcheck_privs, B_FALSE, cr)) == 0)
26406257Smarks 		return (0);
26416257Smarks 
26427163Smarks 	ASSERT(dzp_error && zp_error);
26437163Smarks 
26446257Smarks 	if (!dzpcheck_privs)
2645789Sahrens 		return (dzp_error);
26467163Smarks 	if (!zpcheck_privs)
26476257Smarks 		return (zp_error);
2648789Sahrens 
2649789Sahrens 	/*
2650789Sahrens 	 * Second row
26517163Smarks 	 *
26527163Smarks 	 * If directory returns EACCES then delete_child was denied
26537163Smarks 	 * due to deny delete_child.  In this case send the request through
26547163Smarks 	 * secpolicy_vnode_remove().  We don't use zfs_delete_final_check()
26557163Smarks 	 * since that *could* allow the delete based on write/execute permission
26567163Smarks 	 * and we want delete permissions to override write/execute.
2657789Sahrens 	 */
2658789Sahrens 
26592604Smarks 	if (dzp_error == EACCES)
26607163Smarks 		return (secpolicy_vnode_remove(cr));
26612604Smarks 
26622604Smarks 	/*
2663789Sahrens 	 * Third Row
26646257Smarks 	 * only need to see if we have write/execute on directory.
2665789Sahrens 	 */
2666789Sahrens 
266712273SCasper.Dik@Sun.COM 	dzp_error = zfs_zaccess_common(dzp, ACE_EXECUTE|ACE_WRITE_DATA,
266812273SCasper.Dik@Sun.COM 	    &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
2669789Sahrens 
267012273SCasper.Dik@Sun.COM 	if (dzp_error != 0 && !dzpcheck_privs)
26717163Smarks 		return (dzp_error);
26727163Smarks 
2673789Sahrens 	/*
26747163Smarks 	 * Fourth row
2675789Sahrens 	 */
2676789Sahrens 
267712273SCasper.Dik@Sun.COM 	available_perms = (dzp_working_mode & ACE_WRITE_DATA) ? 0 : VWRITE;
267812273SCasper.Dik@Sun.COM 	available_perms |= (dzp_working_mode & ACE_EXECUTE) ? 0 : VEXEC;
26797163Smarks 
268012273SCasper.Dik@Sun.COM 	return (zfs_delete_final_check(zp, dzp, available_perms, cr));
26817163Smarks 
2682789Sahrens }
2683789Sahrens 
2684789Sahrens int
2685789Sahrens zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
2686789Sahrens     znode_t *tzp, cred_t *cr)
2687789Sahrens {
2688789Sahrens 	int add_perm;
2689789Sahrens 	int error;
2690789Sahrens 
269111935SMark.Shellenbaum@Sun.COM 	if (szp->z_pflags & ZFS_AV_QUARANTINED)
26925331Samw 		return (EACCES);
26935331Samw 
2694789Sahrens 	add_perm = (ZTOV(szp)->v_type == VDIR) ?
2695789Sahrens 	    ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE;
2696789Sahrens 
2697789Sahrens 	/*
2698789Sahrens 	 * Rename permissions are combination of delete permission +
2699789Sahrens 	 * add file/subdir permission.
2700789Sahrens 	 */
2701789Sahrens 
2702789Sahrens 	/*
2703789Sahrens 	 * first make sure we do the delete portion.
2704789Sahrens 	 *
2705789Sahrens 	 * If that succeeds then check for add_file/add_subdir permissions
2706789Sahrens 	 */
2707789Sahrens 
2708789Sahrens 	if (error = zfs_zaccess_delete(sdzp, szp, cr))
2709789Sahrens 		return (error);
2710789Sahrens 
2711789Sahrens 	/*
2712789Sahrens 	 * If we have a tzp, see if we can delete it?
2713789Sahrens 	 */
2714789Sahrens 	if (tzp) {
2715789Sahrens 		if (error = zfs_zaccess_delete(tdzp, tzp, cr))
2716789Sahrens 			return (error);
2717789Sahrens 	}
2718789Sahrens 
2719789Sahrens 	/*
2720789Sahrens 	 * Now check for add permissions
2721789Sahrens 	 */
27225331Samw 	error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
2723789Sahrens 
2724789Sahrens 	return (error);
2725789Sahrens }
2726